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

Хорошее
Что хорошо делают крючки, так это инкапсулируют состояние и разделяют логику. Библиотечные пакеты, такие как react-router и react-redux, имеют более простые и чистые API-интерфейсы благодаря крючкам.

Ниже приведен пример кода, использующего API подключения старой школы.

import React from 'react';
import { Dispatch } from 'redux';
import { connect } from 'react-redux';
import { AppStore, User } from '../types';
import { actions } from '../actions/constants';
import { usersSelector } from '../selectors/users';
const mapStateToProps = (state: AppStore) => ({
  users: usersSelector(state)
});
const mapDispatchToProps = (dispatch: Dispatch) => {
  return {
    addItem: (user: User) => dispatch({ type: actions.ADD_USER, payload: user })
  }
}
const UsersContainer: React.FC<{users: User[], addItem: (user: User) => void}> = (props) => {
  return (
    <>
      <h1>HOC connect</h1>
      <div>
        {
          users.map((user) => {
            return (
              <User user={user} key={user.id} dispatchToStore={props.addItem} />
            )
          })
        }
      </div>
    </>
  )
};
export default connect(mapStateToProps, mapDispatchToProps)(UsersContainer);

Код, подобный этому, раздут. Ввод mapStateToProps и mapDispatchToProps раздражает.

Ниже приведен тот же код, переработанный для использования крючков:

import React from 'react';
import { useSelector, useDispatch } from 'react-redux';
import { AppStore, User } from '../types';
import { actions } from '../actions/constants';
export const UsersContainer: React.FC = () => {
  const dispatch = useDispatch();
  const users: User[] = useSelector((state: AppStore) => state.users);
  return (
    <>
      <h1>Hooks</h1>
      {
        users.map((user) => {
          return (
            <User user={user} key={user.id} dispatchToStore={dispatch} />
          )
        })
      }
    </>
  )
};

Крючки обеспечивают более чистый и простой API. Они также устраняют необходимость оборачивать все в компонент, что является еще одним огромным выигрышем.

Плохое
Массив зависимостей

useEffect Hook принимает аргумент функции и массив зависимостей для второго аргумента.

import React, { useEffect, useState } from 'react';
export function Home() {
  const args = ['a'];
  const [value, setValue] = useState(['b']);
  useEffect(() => {
    setValue(['c']);
  }, [args]);
  console.log('value', value);
}

Приведенный выше код приведет к бесконечному вращению крючка useEffect из-за этого невинного задания:

 const args = ['a'];

При каждом новом рендеринге React сохранит копию массива зависимостей из предыдущего рендеринга. React сравнит текущий массив зависимостей с предыдущим. Каждый элемент сравнивается с помощью Object.is метод определения того, следует ли использовать эффект снова с новыми значениями. Объекты сравниваются по ссылке, а не по значению. Переменные args будут новым объектом при каждом повторном рендеринге и будут иметь в памяти адрес, отличный от предыдущего.

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

Решение, конечно, состоит в том, чтобы использовать больше крючков:

import React, { useEffect, useState, useRef } from 'react';
export function Home() {
  const [value, setValue] = useState(['b']);
  const {current:a} = useRef(['a'])
  useEffect(() => {
    setValue(['c']);
  }, [a])
}

Становится запутанным и неудобным упаковывать стандартный код JavaScript во множество useRef useMemo, или useCallback

Плагин eslint-plugin-react-hooks выполняет разумную работу по удержанию вас на правильном и узком пути, но ошибки не редкость, и плагин ESLint должен быть дополнением, а не обязательным.

Вердикт

Упрощение кода react-redux, упомянутого ранее, является убедительным и приводит к превосходному сокращению чистого кода. Крючки требуют меньше кода, чем у предыдущих сотрудников, и уже один  этот фактор должен сделать крючки несложными. Плюсы Крючков перевешивают минусы, но это не уверенная победа. Крючки-элегантная и умная идея, но бывают случаи, когда их сложно использовать на практике.

SkillPass