Перед созданием приложения React важно избегать нескольких антипаттернов при написании React. В этой статье мы рассмотрим некоторые из этих антипаттернов в React и обсудим, как писать лучший код.
Без лишних слов, давайте начнем.
Prop Drilling
Одна из распространенных ошибок, которую допускают новые разработчики React, — это prop drilling. Это процесс передачи props из родительского компонента в его дочерний компонент.
Давайте рассмотрим этот пример:
Здесь вы можете видеть, что функция приращения передается вниз через несколько слоев компонентов только для того, чтобы быть использованной в компоненте Child. Если приложение увеличивается в размерах или между App и Child появляется больше компонентов, этот шаблон prop drilling приведет к большому количеству избыточного кода, что усложнит его поддержку.
Prop drilling становится проблемой, когда вам приходится передавать props через несколько промежуточных компонентов, которые на самом деле не используют props, а просто служат посланниками для их дальнейшей передачи. Это может усложнить поддержку и понимание кода, особенно по мере роста приложения.
Чтобы сократить prop drilling, вы можете использовать такие методы, как React's Context API или библиотеки управления состоянием, такие как Redux. Эти инструменты позволяют вам управлять и получать доступ к глобальным данным без необходимости вручную передавать props через каждый уровень дерева компонентов.
Index as Key
Использование индекса массива в качестве ключа в React — распространенный антипаттерн, которого разработчикам следует избегать.
React использует ключи для идентификации элементов в списке, а затем эффективно отслеживает изменения во время обновлений. Когда вы используете индекс в качестве ключа, у React нет стабильного идентификатора для всех элементов. Если порядок элементов изменяется или элементы добавляются или удаляются, React может повторно отрисовывать больше элементов, чем необходимо, что может привести к потенциальным проблемам с производительностью.
Плохой подход:
Здесь мы используем индекс каждого элемента в массиве как ключевой prop для списка элементов. Хотя это может показаться удобным, это может привести к проблемам, в основном, когда список динамический и элементы могут быть добавлены, удалены или переупорядочены.
Подход лучше:
Чтобы избежать использования индекса в качестве ключа, разработчики могут использовать уникальные идентификаторы, связанные с каждым элементом списка, такие как идентификаторы базы данных или другие уникальные атрибуты. Использование уникальных идентификаторов гарантирует, что каждый элемент списка имеет стабильную идентичность, даже если порядок списка изменяется или элементы добавляются или удаляются.
Изменение состояния компонента напрямую
Изменение состояния напрямую в React считается антипаттерном. Когда вы изменяете состояние напрямую, React не обнаруживает изменение, что может привести к неожиданному поведению при рендеринге вашего приложения. React использует функцию setState для правильного запуска повторных рендеров при изменении состояния.
Обновления состояния в React асинхронны, то есть React пакетирует несколько обновлений состояния по соображениям производительности. При прямом изменении состояния вы рискуете ввести race conditions и непоследовательные обновления состояния, поскольку React не сможет правильно пакетировать и синхронизировать обновления.
Чтобы избежать анти-паттерна изменяемого состояния, всегда используйте функцию setState для обновления состояния в компонентах React. Это гарантирует, что React сможет правильно отслеживать изменения состояния и запускать повторные рендеры по мере необходимости.
Неиспользование useCallback Hook, когда это было бы полезно
React useCallback Hook возвращает меморизированную функцию обратного вызова, которая помогает улучшить производительность вашего кода. Не использовать useCallback hook, когда это можно сделать, действительно является антипаттерном в React.
Функции, объявленные внутри функциональных компонентов, пересоздаются при каждом рендеринге. Когда эти функции передаются дочерним компонентам как props, это может привести к ненужным повторным рендерингам этих дочерних компонентов. Используя useCallback, вы можете запомнить функцию, чтобы она пересоздавалась только при изменении ее зависимостей, что сокращает ненужные повторные рендеринги.
Вот простой пример:
useCallback()
без useCallback()
Тот, у которого нет хука useCallback, имеет простую функцию обратного вызова, которая будет перерисовываться при каждом изменении состояния, поскольку при каждом рендере создается новая ссылка на функцию. Это может привести к ненужным повторным рендерам ChildComponent, даже когда ParentWithoutCallback перерисовывается из-за изменений состояния.
Компонент с хуком useCallback намного лучше с точки зрения производительности. Ссылка на функцию остается стабильной между рендерами, пока массив зависимостей (второй аргумент useCallback) остается неизменным. Поскольку в этом примере у обратного вызова нет зависимостей, он создается только один раз и сохраняет ту же ссылку между рендерами.
Распространение свойств непосредственно на элементы DOM
Распространение props непосредственно на элементы DOM действительно может привести к добавлению неизвестных атрибутов HTML
Давайте рассмотрим пример:
Компонент Spread распространяет все props (flag и className) на элемент <div> без их фильтрации. И мы знаем, что <div> не имеет атрибута с именем flag в своем определении.
Если какие-либо нераспознанные атрибуты HTML включены в props, переданные в Spread, они будут добавлены в визуализируемый элемент DOM. Это может привести к неожиданному поведению или ошибкам, поскольку неизвестные атрибуты HTML могут некорректно обрабатываться React или браузером.
Лучшим подходом будет создание props специально для атрибута DOM.
В этом подходе свойство domProps специально используется для передачи атрибутов DOM, таких как className. Распространяя только domProps на элемент div, мы гарантируем, что в отрисованный вывод будут включены только распознанные атрибуты HTML.
Props Plowing
Props plowing может возникнуть, когда компоненты имеют большое количество props, которые необходимо передать дочерним компонентам. Это может привести к повторяющемуся и многословному коду, что усложнит его поддержку и понимание.
Использование операторов распространения (…props) может помочь устранить эту проблему за счет сокращения объема повторяющегося кода, необходимого для передачи свойств дочерним компонентам.
Избегайте глубоко вложенных обратных вызовов
Вложенные обратные вызовы, где функции вложены друг в друга, могут сделать код более сложным для понимания и поддержки. Вместо того, чтобы иметь глубоко вложенные функции обратного вызова, лучше разбить сложную логику на более мелкие, более управляемые функции
Плохой подход:
В этом коде извлечение данных из API, их обработка и обновление состояния вложены в функции обратного вызова, что затрудняет отслеживание потока кода.
Подход лучше:
Здесь мы разбили логику на отдельные функции: одна для асинхронной выборки и обработки данных с помощью async/await, а другая для обновления состояния. Такой подход упрощает понимание и поддержку кода.