React Advanced: правильное разделение компонентов
Мечта каждого разработчика — писать меньше кода и возможно сделать его многоразовым.
В React это означает знание того, как правильно отделить логику компонента от его представления.
Легче сказать, чем сделать, не так ли?
В этой статье я покажу вам, как эффективно отделить ваши компоненты, чтобы сделать ваш код максимально пригодным для повторного использования.
Прежде чем мы начнем, давайте взглянем на фундаментальную концепцию связи.
Связь
В информатике связь — это концепция, обозначающая зависимость между двумя или более компонентами. Например, если компонент A
зависит от другого компонента B
, то говорят, что A
связан с B
.
Связывание — враг перемен, поскольку оно связывает воедино вещи, которые могут меняться параллельно.
Из-за этого чрезвычайно сложно изменить одну точку вашего приложения. Прикосновение к одному компоненту может привести к возникновению аномалий в различных частях приложения.
Вам придется потратить время на отслеживание всех частей, которые необходимо изменить, иначе вы задаетесь вопросом, почему все пошло наперекосяк.
Если мы рассматриваем компонент React как чистый элемент представления, мы можем сказать, что он может быть связан со многими вещами:
- Бизнес-логика, определяющая его поведение (хуки, пользовательские хуки и т. д.).
- Внешние сервисы (API, базы данных и т. д.).
- Другой компонент React (например, компонент, отвечающий за управление состоянием формы).
Эта тесная связь при изменении может привести к непредсказуемым побочным эффектам на другие части системы.
Давайте подробнее рассмотрим этот компонент.
import { useCustomerHook } from './hooks';
const Customer = () => {
const { name, surname } = useCustomerHook();
return (
<div>
<p>{name}</p>
<p>{surname}</p>
</div>
);
};
На первый взгляд все кажется хорошо, но на самом деле у нас есть проблема: этот компонент связан с пользовательским хуком useCustomerHook, который извлекает данные о клиентах из внешнего сервиса. Следовательно, наш компонент «Клиент» не является «чистым» компонентом, поскольку он зависит от логики, которая связана не только с представлением его пользовательского интерфейса.
Теперь давайте учтем, что специальный хук useCustomerHook также используется в других компонентах. Чего мы можем ожидать, если решим изменить его? Что ж, нам придется подготовиться к немалой работе, потому что нам придется изменить все компоненты, которые его используют и связаны с ним.
Разделение логики компонента React
Давайте вернемся к предыдущему примеру. Я упоминал, что компонент Customer связан с пользовательским перехватчиком useCustomerHook, который применяет логику выборки для получения данных о клиенте.
Теперь давайте рассмотрим, как отделить логику этого компонента, чтобы мы могли преобразовать его в чистый компонент представления.
import { useCustomerHook } from './hooks';
const Customer = ({name, surname}) => {
return (
<div>
<p>{name}</p>
<p>{surname}</p>
</div>
);
};
const CustomerWrapper = () => {
const { name, surname } = useCustomerHook();
return <Customer name={name} surname={surname} />;
};
export default CustomerWrapper;
Теперь компонент Customer действительно является чистым компонентом представления, поскольку он больше не использует useCustomerHook и обрабатывает только логику пользовательского интерфейса.
Я использовал компонент-оболочку, чтобы отделить логику компонента Customer
. Этот метод известен как Container Components
и позволяет нам изменять пользовательский интерфейс нашего компонента, не беспокоясь о нарушениях базовой логики.
Теперь клиенту нужно заботиться только об отображении презентационной информации. Все необходимые переменные передаются как реквизиты, что позволяет без проблем вложить их в любое место нашего кода.
Однако я все еще не удовлетворен по двум причинам:
- Компонент
CustomerWrapper
по-прежнему связан с пользовательским хуком. Итак, если я решу изменить его, мне все равно придется изменить компонент-оболочку. CustomerWrapper
, чтобы разделить логику, а это значит, что я написал немного больше кода. Мы можем решить эти две проблемы, используя композицию.
Состав
В информатике композиция — это концепция, обозначающая объединение двух или более элементов для создания нового. Например, если у нас есть две функции f
и g
, мы можем скомпоновать их, чтобы создать новую функцию h
, которая является композицией f
и g
.
const f = (x) => x + 1;
const g = (x) => x * 2;
const h = (x) => f(g(x));
Мы можем применить ту же концепцию и к пользовательским хукам. Фактически, мы можем скомпоновать два или более пользовательских хуков, чтобы создать новый.
const useCustomerHook = () => {
const { name, surname } = useCustomer();
const { age } = useCustomerAge();
return { name, surname, age };
};
Таким образом, пользовательский перехват useCustomerHook состоит из пользовательских перехватчиков useCustomer
и useCustomerAge
.
Используя композицию, мы можем отделить логику компонента React без необходимости создания компонента-обертки. Для удобного применения композиции воспользуемся библиотекой act-hooks-compose
Давайте посмотрим, как мы можем применить композицию к нашему примеру.
import composeHooks from 'react-hooks-compose';
import { useCustomerHook } from './hooks';
const Customer = ({name, surname}) => {
return (
<div>
<p>{name}</p>
<p>{surname}</p>
</div>
);
};
export default composeHooks({useCustomerHook})(Customer);
Теперь компонент Customer
действительно является чистым компонентом представления. Он не связан с какими-либо пользовательскими перехватчиками и обрабатывает только логику пользовательского интерфейса. Более того, вам не нужно было создавать какие-либо дополнительные компоненты для разделения логики. Фактически, композиция позволяет создать более чистый и читаемый компонент.
Еще одно преимущество этого метода заключается в том, насколько легко оно упрощает тестирование клиентского компонента. Вам не нужно беспокоиться о тестировании бизнес-логики; вам нужно только протестировать логику пользовательского интерфейса. Кроме того, вы можете протестировать пользовательские хуки отдельно.
В заключение давайте посмотрим, что произойдет, если вы решите добавить новый пользовательский крючок, который добавляет некоторую логику к компоненту Customer
, например пользовательский крючок, который обрабатывает регистрацию информации о клиенте.
import composeHooks from 'react-hooks-compose';
import { useCustomerHook, useLoggerHook } from './hooks';
const Customer = ({name, surname, age}) => {
return (
<div>
<p>{name}</p>
<p>{surname}</p>
</div>
);
};
export default composeHooks({
useCustomerHook,
useLoggerHook
})(Customer);
Отлично, вы добавили пользовательский перехват useLoggerHook
в компонент Customer
без необходимости изменять сам компонент.
Это связано с тем, что useLoggerHook
был составлен с использованием useCustomerHook
.
Заключение
В этой статье мы рассмотрели, как отделить логику компонента React с помощью композиции перехватчиков, превратив ее в чистый компонент представления. Искусство композиции хуков дает нам мощный инструмент для повышения модульности и удобства сопровождения наших компонентов React.
Четко отделяя бизнес-логику от представления, мы делаем наши компоненты более читабельными и простыми для тестирования. Эта методология способствует повторному использованию кода и масштабируемости приложений React, позволяя разработчикам сосредоточиться на создании более чистых и производительных компонентов.
Если у вас есть предложения или вопросы по этой теме, поделитесь ими в комментариях ниже. И если эта статья оказалась для вас полезной, не забудьте поделиться ею со своими коллегами-разработчиками!