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;

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

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

Надеюсь, что эта статья кому-нибудь может. Можете писать свои примеры полезных библиотек в комментарии, буду рад узнать что-то новое.

Комментарии (21)


  1. vtvz_ru
    01.08.2018 17:54
    +6

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


    Замечание по поводу первой библиотеки: использовать в списках ее категорически нельзя. Это может очень плохо сказаться на производительности, особенно на больших списках, т.к. 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


    1. faiwer
      01.08.2018 18:43
      +3

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

      Народ наткнувшись на это "не рекомендуется" начинает бояться так делать, и начинает городить чёрт знает что. Топик-стартер вот генерирует новые 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" совершенно не обязательно должны быть статичными.


    1. Dartess
      01.08.2018 20:53

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


      1. vtvz_ru
        02.08.2018 11:40

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


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

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


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


        1. Dartess
          02.08.2018 11:53

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


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

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


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


    1. snapoak Автор
      01.08.2018 23:55

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


  1. faiwer
    01.08.2018 18:22
    +7

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


    1. kylt_lichnosti
      01.08.2018 23:58

      Зато шторм не ругается.


  1. Valery4
    02.08.2018 00:04

    Имея в проекте lodash вообще не понимаю зачем бы мне понадобился nanoid.


    1. Valery4
      02.08.2018 00:11

      И да, я не про списки и Реакт. А вообще если бы мне понадобились уникальные ID.
      Например когда я делал мок данных которые потом буду приходить с сервера с уникальными ID.


    1. snapoak Автор
      02.08.2018 00:12

      1КБ на дороге не валяется!
      P.S. спасибо, никогда не смотрел в сторону lodash, так как приятель сказал, что ramda ramda ramda


      1. Valery4
        02.08.2018 00:14

        Не 2, а 2.2K — 1.2K = 1K.
        А если в сжатом виде 915 — 638 = 277
        Внимательно прочитайте ещё раз мой первый коммент.
        Имея оверхед в 277 байт — я не буду подключать ради их экономии более лёгкую библиотеку.
        Допустим tree shaking работает как надо.


        1. snapoak Автор
          02.08.2018 00:15
          +1

          Опечатался, прошу прощения.
          Про размер ирония была(часто иронизирую про размер), конечно, если используете lodash, в nanoid смысла никакого нет


      1. Valery4
        02.08.2018 00:19

        А я про Ramda никогда не слышал.


        1. snapoak Автор
          02.08.2018 00:22

          Библиотека для функционального программирования на JS. Много интересных штук, позволяет писать чистый код (описание многих ФП библиотек на JS)


    1. Blooderst
      02.08.2018 10:17

      А чем Math.random не угодил? Для цели генерации случайных ключей в реактовском списке (вон из профессии!) полностью подходит — шанс повторения ничтожно мал, все генерируемые значения можно считать уникальными. Экономия в 638 (915) байт.


  1. AxisPod
    02.08.2018 09:42
    +2

    Nanoid, что за бред? Убить всю производительность React, пусть тормозит, процы ныне мощные, надо чем-то нагружать? Тут я бы ещё понял, если эта либа вычисляла хэш для объекта, а случайное число, это уже полный бред.


    1. vlreshet
      02.08.2018 14:34
      +1

      Если откинуть бредовость конкретно для реакта — 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», и, мол, чуток больший диапазон символов. Но тем не менее


  1. Nookie-Grey
    02.08.2018 14:23

    Пугает, что кто то еще ставит + к рейтингу для этой статьи…


    1. faiwer
      02.08.2018 16:31

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


      1. snapoak Автор
        02.08.2018 17:11
        +1

        Не забил ;(