До React Conf 2018 функциональные компоненты были без сохранения состояния и унаследовали свои возможности от родительского компонента, который был расширен из класса React Component. Эти компоненты являются полностью унаследованными и отображаемыми данными.

В мире, где существуют хуки и контекстный API, компонент React на основе классов становится динозавром, и не зря.

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

"это слишком

При работе с компонентом на основе классов на свойства, состояние, ссылки и методы ссылаются в классе с помощью ключевого слова this. Как человек, который немного работал с this (простите за каламбур), постоянное использование this может стать повторяющимся.

При использовании методов в вашем компоненте, основанном на классе, вы часто можете столкнуться с необходимостью использовать стрелочные функции или привязать функцию к контексту this класса. Хотя стандартной практикой является сохранение ссылки на такие важные элементы, как this.state и this.setState, не лучше ли иметь возможность напрямую ссылаться на переменную состояния. и установить состояние, не беспокоясь об this вообще?

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

Интуитивное управление состоянием

Вместо того, чтобы постоянно вызывать this.setState ({name: value}), ловушка useState позволяет нам делегировать определенную функцию установки состояния, которую мы можем повторно использовать, и переменную для ссылки на государственное значение. Как и компоненты на основе классов, вы не ограничены тем, какие типы Javascript вы можете хранить в своем состоянии.

Вам также не обязательно следовать стандарту «setX» номенклатуры . Вызовите функцию установки «updateText». Хотя я бы рекомендовал по большей части использовать стандарт «setX» для написания ваших сеттер-функций, я могу представить себе случаи, когда другое словоблудие могло бы быть более наглядным.

НО ЖДАТЬ! Что если я хочу использовать уже существующее состояние? Что ж, не бойтесь. Функция-установщик, возвращаемая ловушкой useState, также принимает функцию, где первым параметром является предыдущее состояние.

Замечательно, мы использовали useState. Давайте теперь добавим функцию POST для сохранения наших данных, похоже, это очень важный материал, который мы хотели бы сохранить для нашего пользователя.

Если вы знакомы с оптимизацией компонентов React, возможно, вы заметили небольшую проблему в приведенном выше примере - функция, переданная обработчику onClick кнопки «POST to Server», не запоминается и будет воссоздаваться при каждом рендеринге. Хотя в этом небольшом примере последствия этого не заметны для конечного пользователя, это хорошее место, чтобы предложить панацею от этой проблемы - нашу следующую ловушку, useCallback.

Функции памяти

Давайте посмотрим на ловушку useCallback в действии, оптимизируя наш предыдущий пример, прежде чем подробно рассказывать.

Не слишком сумасшедший, но что здесь происходит? Первым аргументом, передаваемым в ловушку useCallback, является наша функция для запоминания. В этой функции мы будем ссылаться на две из наших переменных состояния: число и текст. Таким образом, мы передаем их в наш массив зависимостей, второй аргумент useCallback.

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

Здесь следует понимать, что useCallback запоминает нашу функцию, но не запоминает значение, возвращаемое вызываемой функцией. Как мы можем запоминать значение? Давайте представим нашу следующую ловушку, useMemo.

Памятные ценности

Хук useMemo очень похож на useCallback, с той разницей, что useMemo запоминает значения. Давайте посмотрим на это в действии.

Обычно вы используете useMemo для запоминания значений, которые слишком дороги для повторной оценки каждого повторного рендеринга компонента. Разработчик решает, когда лучше всего использовать эту ловушку.

Жизненные циклы функциональных компонентов

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

Используя хуки, мы можем выполнять то, что предопределенные жизненные циклы React делают для нас, гораздо более читаемым и детализированным способом. Мы действительно контролируем, как наш компонент реагирует на изменения.

Хук useEffect - это то, как мы подходим к жизненным циклам наших функциональных компонентов. Его простота включает в себя возможность полностью заменить (почти, но мы еще доберемся) наши традиционные основанные на классах методы жизненного цикла компонентов React.

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

Вы можете иметь столько эффектов в своем компоненте, сколько необходимо. Давайте посмотрим, как можно использовать useEffect в функциональном компоненте, чтобы получить тот же результат, что и при использовании componentDidMount в компоненте на основе классов.

Что происходит наверху? Эффект ищет элементы в массиве зависимостей, которые должны «зависеть» от срабатывания. Поскольку мы предоставили пустой массив, этот эффект сработает один раз при монтировании компонента, а не снова, поскольку мы предоставили пустой массив зависимостей. Возникает вопрос - зачем вообще передавать массив, если он пуст? Почему бы просто не предоставить второй аргумент для ловушки useEffect?

Не предоставляя второй аргумент, мы сообщаем эффекту, прямо противоположному запуску один раз - мы говорим ему запускаться каждый раз при повторном рендеринге и игнорировать зависимости .

Имея это в виду, я не могу оставить без внимания один из самых распространенных вариантов использования componentDidMount - вызовов API! Интуитивно вы можете сделать следующее:

Мой код VSCode достаточно умен, чтобы сказать, что это неверно. Это хорошо усвоено! Обратные вызовы эффектов синхронны, чтобы предотвратить состояние гонки. Означает ли это, что мы не можем выполнять в них асинхронные операции? Неа! Посмотрим как.

В приведенном выше примере мы могли бы написать любой из приведенных выше эффектов (оба являются одним и тем же, но написаны по-разному). Лично я предпочитаю первый вариант.

Теперь поговорим о componentWillUnmount. Это очень распространенный жизненный цикл, используемый для удаления слушателей или интервалов, созданных компонентом на основе классов. Первый аргумент ловушки useEffect, функция, может при желании возвращать функцию очистки, которая выполняет эту отмену подписки.

В этом примере мы добавляем прослушиватель событий при монтировании. При размонтировании компонента Orphan мы предоставили функцию обратного вызова для вызываемого эффекта, которая удаляет прослушиватель событий.

Жизненный цикл componentDidUpdate возвращает нам предыдущие свойства и состояния для работы. Работа с этим жизненным циклом обычно вызывает наибольшее раздражение, поскольку нам приходится создавать множество условий if, чтобы запускать побочные эффекты для обновлений состояния и поддержки. Используя хуки, эта проблема полностью решается, мы просто объявляем эффект, который по существу подписывается на реквизиты и состояние, переданные как зависимости. Одна из проблем, с которыми мы здесь сталкиваемся, заключается в том, что у нас нет доступа к предыдущим значениям свойств или состояний, и, честно говоря, React не предлагает внутреннего решения для этого.

Я обычно полагаюсь на специальный хук, чтобы воспроизвести эту функциональность, причем очень легко. Хук usePrevious - это стандартный хук сообщества, который не является собственностью React. Существуют пакеты, предлагающие эту функциональность, но я предпочитаю просто создать папку ловушек utils, в которую я помещаю нужные мне перехватчики в моем проекте, куда я копирую код usePrevious. Это одна из причин, по которой я говорил ранее, что хуки React почти заменяют компонент React на основе классов - на самом деле, с помощью help.

Следующий метод жизненного цикла, shouldComponentUpdate, на самом деле является одним из наиболее менее надежных для предотвращения обновлений компонентов. Новый стандарт запоминания компонентов для предотвращения ненужного повторного рендеринга - это вообще не ловушка, а функция React.memo.

К сожалению, собственного API, заменяющего componentDidCatch, не существует, хотя команда React заявила, что он находится в разработке. А пока я бы посоветовал использовать пакет response-error-граница. Синтаксис также чище, чем при использовании componentDidCatch.

В заключение

Есть еще несколько хуков и преимуществ, которые не были рассмотрены в этой статье, в частности, ловушка useReducer для поклонников Redux, встроенная в React.

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

Всего наилучшего, счастливого Реагирования.