4 библиотеки, упрощающие жизнь React-разработчика

image

В этой статье я расскажу о четырех библиотеках, которые сделают вашу жизнь проще. По крайней мере, я на это надеюсь

1) Nanoid


Ссылка на github
Это маленькая библиотека, которая имеет всего одну функцию — генерация уникального id. Она может быть полезна в массе случаев, как любой другой генератор случайных последовательностей символов. Неоспоримые плюсы: простота и крошечный размер — 143 байта.

Конечно, если вы используете, к примеру, lodash, то вы можете использовать метод uniqueId().
Использование максимально просто:
import nanoid from 'nanoid'
size = 8
id = nanoid(size) //cjF38yYc

UPD:
НИКОГДА не используйте nanoid() для индексации списков или любых других повторяющихся элементов с помощью key в ReactJS. Так как если использовать этот метод, каждый раз при обновлении компонента React будет думать, что все элементы новые и будет их перерендерить, что очень плохо скажется на производительность и вообще противоречит смыслу key.

Подробнее о key: reactjs.org/docs/lists-and-keys.html#keys
Еще подробнее о key: habr.com/company/hh/blog/352150

Пример очень плохого использования nanoid:
import nanoid from 'nanoid'
import React from 'react'

const ListDrinks = props=>{
    const drinks = ['rum','bear','vodka']
    return(
        <ul>
            {drinks.map((values)=>{
                //Вот так делать нельзя!
                return(
                    <li key={nanoid(8)}>{values}</li>
                )
            })}
        </ul>
    )
}
export default ListDrinks

Пример нормального использования nanoid:
import React, { Component } from 'react';
import nanoid from 'nanoid'

class InputWithLabel extends Component {
  constructor(props){
    super(props)
    this.id = nanoid()
  }
  render() {
    return (
      <div>
        <label htmlFor={this.id}>My label</label>
        <input id={this.id} type="text"/>
      </div>
    );
  }
}

export default InputWithLabel;


То, как делать нельзя я узнал от добрых людей в комментариях, за что им отдельное, большое спасибо!

2) Classnames


Ссылка на github
Эта библиотека для простого условного объединения имен классов. Пользоваться ей не намного сложнее, чем предыдущей библиотекой.

Пример простого использования:

import cn from 'classnames'

cn('menu','active')//'menu active'
let isActive = true
cn('menu',{'active':isActive})//'menu active'
isActive = false
cn('menu',{'active':isActive})//'menu'

Лично для меня эта библиотека является обязательной в любом React-приложении. Конечно, до того момента, пока я не найду более удобный инструмент.

3) Formik и Yup


Ссылка на github(Formik)
Ссылка на github(Yup)
В разговоре об упрощении чего-либо в React нельзя не упомянуть работу с формами. Наверное, каждый начинающий React-developer в один прекрасный момент понимал, как он ненавидит работу с формами. Когда приходит это понимание, стоит незамедлительно искать спасительную пилюлю.

Для меня этой пилюлей стали Formik и Yup.

Formik — библиотека, помогающая работать с формами. Она упрощает получение данных из формы, валидацию данных, вывод сообщений об ошибках и многое другое.

Yup — библиотека, которая является валидатором для модели, которую мы сами и создаем с помощью Yup.

Для сколько-нибудь полного описания этой связки нужна отдельная статья, но я попытаюсь показать с высоты птичьего полета, что они из себя представляют.

Код примера можно запустить тут: Пример

Первым делом создадим схему:

import * as Yup from "yup";

const BasicFormSchema = Yup.object().shape({
  email: Yup.string()
    //Проверяем, корректный ли адрес.
    //Если нет, то выводится сообщение в скобках
    .email("Invalid email address")
    //не сабмитим, если поле не заполнено
    .required("Required"),
  username: Yup.string()
    //минимальная длина - 2 символа
    .min(2, "Must be longer than 2 characters")
    //максимальная длина - 20 символов
    .max(20, "Nice try, nobody has a first name that long")
    .required("Required"),
  password: Yup.string()
    .min(8, "Must be longer than 8 characters")
    .required("Required")
});
export default BasicFormSchema;

В коде выше мы определили схему, которая по сути — объект. Она имеет три поля: email, username и password. Каждому из полей мы определили некоторые проверки.

Одним из способов использования Formik является элемент <Formik/>, который имеет множество разных свойств, один из которых render.

import React from "react";
import { Formik, Field, Form } from "formik";
import BasicFormSchema from "./BasicFormSсhema";

const SignUp = () => (

  <div className="container">
    <h1>Sign up</h1>
    <Formik
      //инициализируем значения input-ов
      initialValues={{
        email: "",
        username: "",
        password: ""
      }}
      //подключаем схему валидации, которую описали выше
      validationSchema={BasicFormSchema}
      //определяем, что будет происходить при вызове onsubmit
      onSubmit={values => {
        setTimeout(() => {
          alert(JSON.stringify(values, null, 2));
        }, 500);
      }}
      //свойство, где описывыем нашу форму
      //errors-ошибки валидации формы
      //touched-поля формы, которые мы "затронули",
      //то есть, в которых что-то ввели
      render={({ errors, touched }) => (
        <Form className="form-container">
          <label htmlFor="email">Email</label>
          <Field
            name="email"
            placeholder="mtarasov777@gmail.com"
            type="email"
          />
          
          {//если в этом поле возникла ошибка и 
          //если это поле "затронуто, то выводим ошибку
            errors.email &&
            touched.email && <div className="field-error">{errors.email}</div>}

          <label htmlFor="username">Username</label>
          <Field name="username" placeholder="snapoak" type="text" />

          {errors.username &&
            touched.username && (
              <div className="field-error">{errors.username}</div>
            )}

          <label htmlFor="password">Password</label>
          <Field name="password" placeholder="123456qwe" type="password" />

          {errors.password &&
            touched.password && (
              <div className="field-error">{errors.password}</div>
            )}

          <button type="submit">Submit</button>
        </Form>
      )}
    />
  </div>
);

export default SignUp;

Код простой, я снабдил его комментариями, поэтому, думаю, вопросов возникнуть не должно.
Если же они возникли, то в ГитХаб репозитории имеется отличная документация, также можете задавать вопросы в комментариях.

Вот и конец. Я знаю, что есть много отличных библиотек, для работы с формами, какие-то кажутся вам лучшими, какие-то худшими. Я выразил тут личное мнение.

Надеюсь, что эта статья кому-нибудь может. Можете писать свои примеры полезных библиотек в комментарии, буду рад узнать что-то новое.
AdBlock has stolen the banner, but banners are not teeth — they will be back

More
Ads

Comments 25

    +8

    Статья немного не формата хабра, но ладно… Немного моих мыслей.


    Замечание по поводу первой библиотеки: использовать в списках ее категорически нельзя. Это может очень плохо сказаться на производительности, особенно на больших списках, т.к. ReactJS будет пересоздавать элементы на каждый чих. Тогда уж лучше вообще ключи не использовать, чем рандомные. На крайний случай, индекс массива, хотя это тоже не рекомендуется. А лучше всего использовать статичные IDшники, либо, как в Вашем примере, сам элемент:


    Код
    import React from 'react'
    
    const ListDrinks = props => {
        const drinks = ['rum','bear','vodka']
        return(
            <ul>
                {drinks.map((value)=>{
                    return(
                        <li key={value}>{value}</li>
                    )
                })}
            </ul>
        )
    }
    export default ListDrinks

    Если говорить про библиотеку для генерации классов, то я использую эту. Люблю BEM. Написал обертку-декоратор для этой либы, который можно использовать примерное так:


    Еще код
    import React from 'react';
    
    import bemHelper from './bemHelper';
    
    @bemHelper('timeline')
    export default class Timeline extends React.PureComponent {
        render() {
            const {
                bemHelper
            } = this.props;
    
            return <div className={bemHelper()}>
                <div className={bemHelper('element', 'modifier')}></div>
            </div>;
        }
    }

    Просто нереально удобно // Знаю про CSS-in-JS, но у нас используется SCSS со всеми вытекающими


    Для форм использую react-form. Он немного сложен, но гибок и удобен.

      +6
      На крайний случай, индекс массива, хотя это тоже не рекомендуется

      Народ наткнувшись на это "не рекомендуется" начинает бояться так делать, и начинает городить чёрт знает что. Топик-стартер вот генерирует новые ID при каждом render-е. А один мой коллега очень сильно извратил кодовую базу, добавляя ID в статичные списки сущностей.


      На самом же деле надо просто понять проблему. И тогда оказывается, что в ряде случаев использование индекса в массиве в качестве key очень даже уместно. По сути, если:


      • у наших перечисляемых объектов нет своего ID и никакая комбинация из этих полей не может быть уникальной.
      • в списке невозможны вставки, удаления, перемещения. Сами элементы могут вполне изменяемыми, но вот их взаимное расположение должно быть статичным.

      … то мы можем смело использовать key={index} ни о чём не переживая. По правде говоря, даже не удовлетворяя этим двум пунктам иногда можно использовать key={index}, но это уже совсем другая история (с).


      P.S. автор статьи по вашей ссылке не прав в пункте "the list and items are static–they are not computed and do not change" + "When all of them are met, you may safely use the index as a key.". Потому что "items" совершенно не обязательно должны быть статичными.

        0
        Кажется, вы переизобрели react-bem-classes :) прекрасная штука, очень удобно работать в т.ч. с модификаторами, плюс миксовать блоки и элементы.
          0

          Ну почти переизобрел (Библиотека не моя, просто пользуюсь. Только декоратор самописный). Мне кажется, react-bem-helper немного более гибкий. По крайней мере я не заметил в Вашей библиотеке подобных конструкций:


          bemHelper('element', {
            mod1: true,
            mod2: false,
            mod3: function() { return false; }
            'mod5 mod6': function() { return true; }
          });

          Для меня замыкания не столь необходимы, но вот передавать объект с модификаторами, где все контролируется Boolean значениями, это просто мегаудобно. Прям как доктор прописал.


          Посмотрите в сторону этой библиотеки. Там много чего интересного есть. А декоратор написать не сложно.

            +1

            Она не моя, просто остановился на ней, когда выбирал.


            this.element('element', {
              mod1: true,
              mod2: false,
              mod3: "with-value",
            });

            вот так работает, т.е. модификаторы либо булевы, либо со значением. В документации нет примера с модификаторами элемента, только пример с блоком, но работает так же (сама возможность передавать модификаторы объектом описана). Передачи коллбэка, вроде как, нет, и ИМХО это был бы лишний функционал.


            Того что есть хватает за глаза. Подметил просто, что react-bem-helper с декоратором стал похож на react-bem-classes.

          0
          Спасибо за подробный ответ! Узнал много нового!
          +11

          А что вы хотели показать примером для Nanoid? Вы всякий раз генерируете новый ID для элементов списка. Чего ради? По сути вы показываете React, что у вас всякий раз НОВЫЕ элементы. React не соотносит их со старыми instance-ми компонент, а генерирует новые. Полностью с нуля формирует всё, что касается VDom. Всякий раз. Даже если не поменялось вообще ничего. Зачем?

            –5
            Зато шторм не ругается.
            0
            Имея в проекте lodash вообще не понимаю зачем бы мне понадобился nanoid.
              0
              И да, я не про списки и Реакт. А вообще если бы мне понадобились уникальные ID.
              Например когда я делал мок данных которые потом буду приходить с сервера с уникальными ID.
                –1
                1КБ на дороге не валяется!
                P.S. спасибо, никогда не смотрел в сторону lodash, так как приятель сказал, что ramda ramda ramda
                  0
                  Не 2, а 2.2K — 1.2K = 1K.
                  А если в сжатом виде 915 — 638 = 277
                  Внимательно прочитайте ещё раз мой первый коммент.
                  Имея оверхед в 277 байт — я не буду подключать ради их экономии более лёгкую библиотеку.
                  Допустим tree shaking работает как надо.
                    +1
                    Опечатался, прошу прощения.
                    Про размер ирония была(часто иронизирую про размер), конечно, если используете lodash, в nanoid смысла никакого нет
                    –1
                    А я про Ramda никогда не слышал.
                      +1
                      Библиотека для функционального программирования на JS. Много интересных штук, позволяет писать чистый код (описание многих ФП библиотек на JS)
                        0
                        Я посмотрел, буду иметь в виду. Мне непонятна реакция на мой комментарий.
                        Как будто это преступление какое — чего-то не знать. Сразу вон из профессии, как уже писали выше.
                    –3
                    А чем Math.random не угодил? Для цели генерации случайных ключей в реактовском списке (вон из профессии!) полностью подходит — шанс повторения ничтожно мал, все генерируемые значения можно считать уникальными. Экономия в 638 (915) байт.
                    +3
                    Nanoid, что за бред? Убить всю производительность React, пусть тормозит, процы ныне мощные, надо чем-то нагружать? Тут я бы ещё понял, если эта либа вычисляла хэш для объекта, а случайное число, это уже полный бред.
                      +3
                      Если откинуть бредовость конкретно для реакта — Nanoid всё-равно остаётся дичью. Тянуть целую библиотеку (пускай и такую маленькую) вместо одной строчки vanilla js

                      Math.random().toString(32).substr(2, 12) //bgt1799p4kg
                      Math.random().toString(32).substr(2, 12) //a5mepkmd5
                      Math.random().toString(32).substr(2, 12) //0fn8ujjhmfg
                      


                      UPD. досмотрел, они ещё обещают «secure», и, мол, чуток больший диапазон символов. Но тем не менее
                      0
                      Пугает, что кто то еще ставит + к рейтингу для этой статьи…
                        0

                        Что характерно — автор критику прочитал… и забил :)

                          +2
                          Не забил ;(
                        0

                        Поясните, что за нужда генерить id на клиенте? Разве они не должны приходить с бэка (в приложении здорового человека)? И вроде логика общая такова: существующая в хранилище модель уже имеет id, если модель создается на клиенте — id у нее нет и он появляется при сохранении в DAL. Зачем вообще какие-то ключи на фронте генерить??

                          0
                          Поясните, что за нужда генерить id на клиенте? Разве они не должны приходить с бэка (в приложении здорового человека)?

                          Сущности, которые нужно перебирать, могут вообще никак не быть связанными с backend-ом. Я даже больше скажу — не всякое приложение вообще требует backend-а.

                          0
                          По поводу classnames есть альтернатива которая работает производительнее: html-classes, вот тест

                          Only users with full accounts can post comments. Log in, please.