Мы можем считать React королем веба, когда речь идет о разработке фронтенда. Это легкая библиотека JavaScript, чья функциональность и гибкость завоевали сердца программистов по всему миру. Молниеносная скорость веб-приложений, многоразовые компоненты, позволяющие сэкономить время на разработку, и виртуальный DOM, позволяющий обновлять отдельные части приложения по мере необходимости — вот лишь несколько примеров убийственных функций, определяющих успех React. В результате в 2021 году он стал самым используемым веб-фреймворком, по данным Statista:
Однако такая популярность React не спасает программистов, использующих его в своих проектах, от плохих практик разработки. React позволяет разработчикам создавать компоненты и повторно использовать их в своем коде или даже импортировать в другие проекты. Без должного внимания к качеству кода плохие привычки программирования рискуют снизить сопровождаемость системы и превратить преимущества React в пыль. Сегодня мы рассмотрим несколько примеров анти-паттернов React, избежание которых поможет вам обеспечить первоклассное качество ваших веб-приложений.
Чем опасны анти-паттерны и как их своевременно обнаружить
Языки программирования и фреймворки для веб-разработки — коварная штука. Может показаться, что они делают то, что вы хотите, но вопрос в том, как убедиться, что вы используете их правильно. Вы можете импортировать необходимые вам библиотеки, создать нужные вам компоненты и отобразить на экране все, что вы хотите, но это не означает, что нет места для улучшения. Более того, это не означает, что ваш проект не развалится, если вы решите повторно использовать некоторые из своих компонентов в другом месте.
Если вы создаете кусок кода или компонент, который вы или любой другой программист сможете использовать повторно в дальнейшем без особых усилий, это признак хорошего паттерна. Если код легко просматривать, поддерживать, импортировать и отлаживать, то шансы на то, что вы используете хороший паттерн, еще выше. Антипаттерном можно считать все, что работает противоположным образом. Даже опытные разработчики могут стать жертвами антипаттернов, если не будут уделять им должного внимания.
Читайте также Цена популярности. Сильные и слабые стороны ReactJS и React Native
К счастью, есть некоторые признаки, которые помогают обнаружить антипаттерны в коде React. Например, когда вы создаете веб-приложение с использованием этой библиотеки, вы хотите, чтобы разные его части были взаимосвязаны. Это помогает обеспечить желаемое состояние всего приложения, когда все компоненты зависят друг от друга. Когда вы нарушаете это правило, используя, например, хук useRef, который не принимает массивы зависимостей, вы увеличиваете вероятность потенциальных проблем, поэтому лучше быть осторожным в таких сценариях.
Другой пример — чрезмерная вложенность. Например, вполне нормальным может показаться создание родительского компонента, у которого есть дочерний компонент, если этого требует специфика компоновки приложения. Дочерний компонент может быть родителем другого компонента, и так далее. Эта кроличья нора может быть довольно глубокой, в зависимости от сложности вашего React-приложения. Проблема в том, что когда в, скажем, дочернем компоненте номер 10 возникает ошибка, вам придется пройтись по всему дереву его родителей, чтобы найти ее источник.
Примеры антипаттернов React, которых лучше избегать
Когда вы используете слишком много вложенных компонентов, ваш код может превратиться в настоящую головную боль. Давайте рассмотрим пример, который мы уже упоминали, поскольку он довольно часто встречается у неопытных разработчиков, пытающихся войти в мир разработки React JS. Например, вы можете вложить свои компоненты React следующим образом:
import { useState } from 'react';
export const ImAParent = () => {
const [count, setCount] = useState(0);
const ImAChild = () => (
<div>This is a child component</div>
);
return (
<div>
<ImAChild />
<button onClick={() => setCount(count + 1)}>Counting</button>
</div>
);
};
Здесь не о чем беспокоиться, скажете вы. В конце концов, нет другого места для использования дочернего компонента, кроме как внутри родительского компонента. К сожалению, у такого подхода есть некоторые скрытые недостатки. Прежде всего, вы рискуете столкнуться с проблемами производительности. Мы использовали довольно простой пример, но в реальной жизни ваш React-код может состоять из десятков вложенных компонентов. Каждый раз, когда возникает необходимость перерисовать приложение, родительский компонент должен будет выполнить код, относящийся к дочернему компоненту. Даже если в дочернем компоненте нет новых данных для отображения, родительский компонент будет многократно выполнять функцию объявления. Чем больше вложенных компонентов содержит ваш код, тем больше вычислительных ресурсов будет потрачено на эту бессмысленную задачу. Представьте, какой ущерб это может нанести, если вы решите импортировать этот код в другие проекты. Поэтому важно не объявлять компоненты внутри их родительских компонентов. В качестве альтернативы вы можете использовать что-то вроде этого:
import { useState } from 'react';
const ImAChild = () => (
<div>This is a child component</div>
);
export const ImAParent = () => {
const [count, setCount] = useState(0);
return (
<div>
<ImAChild />
<button onClick={() => setCount(count + 1)}>Counting</button>
</div>
);
};
Недооценка результатов сложных вычислений — еще одна привычка, которой лучше избегать. Давайте представим, что вы хотите создать приложение, которое работает с большими данными и полагается на сложные вычисления. В этом сценарии вы, возможно, решите использовать код React, который выглядит следующим образом:
import { useState } from 'react';
export const BigDataAppComponent = () => {
const [count, setCount] = useState(0);
const bigDataStuff = someComplexCalculations();
return (
<div>
<button onClick={() => setCount(count + 1)}>Counting</button>
</div>
);
};
Поверьте, этот компонент выполняет очень тяжелые вычисления. Проблема в том, что этот прекрасно выглядящий код может вызвать серьезные проблемы с производительностью. Если какое-либо действие изменит состояние компонента, ему придется снова выполнять все эти вычисления, даже если новых данных не будет. Если вы повторно используете этот код в разных частях вашего приложения React, вы можете столкнуться с серьезными проблемами. К счастью, вы всегда можете импортировать и использовать хук useMemo, который может запомнить результаты предыдущих вычислений и избавить вас от траты вычислительной мощности, если данные не меняются:
import { useState, useMemo } from 'react';
export const BigDataAppComponent = () => {
const [count, setCount] = useState(0);
const bigDataStuff = useMemo(() => someComplexCalculations(), []);
return (
<div>
<button onClick={() => setCount(count + 1)}>Counting</button>
</div>
);
};
Читайте также React Native для мобильной разработки. Убить двух зайцев одним выстрелом
Управление состояниями — одна из самых сложных задач в React-приложениях, которая влияет на поддерживаемость и масштабируемость. Чтобы избежать излишней сложности, может быть полезно максимально ограничить свое намерение хранить переменные в состоянии. Для этого вы можете следовать концепции производного состояния. Она подразумевает, что вы должны предпочесть использование переменных, которые вы можете вычислить «на лету». Например, если у вас большая форма, содержащая тонны флажков, вы можете определить, отмечены некоторые из них или нет, перебирая массив items и фильтруя их каждый раз, когда возникает необходимость в повторном рендеринге компонента. Следование патчу производных состояний — надежный способ синхронизировать данные, с которыми работает ваше приложение, при внесении новых изменений.
Выводы
React — это довольно мощный инструмент разработки. Как и любой другой инструмент, он может быть использован для создания красивых вещей, но в неумелых руках он может внести недостатки в проект. Даже если кажется, что все работает отлично, некоторые части кода могут состоять из безобидных на первый взгляд анти-паттернов, что приведет к снижению производительности и потенциальным проблемам с обслуживанием. Хорошая новость заключается в том, что опытные программисты прекрасно знают о них, поэтому вам не о чем беспокоиться, если вы решили сотрудничать с надежной командой разработчиков программного обеспечения.