
Рефакторинг приложения React и Redux может быть сложной задачей, но при тщательном планировании и выполнении он может привести к созданию более эффективной и удобной в сопровождении кодовой базы. Одна проблема, с которой я столкнулся в нескольких компаниях, — это чрезмерное сверление пропеллеров. Детализация реквизита — это распространенный шаблон в React, когда реквизиты передаются от родительского компонента к дочернему компоненту, а затем к его дочерним компонентам и так далее. Это может привести к глубоко вложенным компонентам с длинными цепочками реквизитов, что затруднит управление и обслуживание.
Это проблематично по ряду причин, включая ненужный повторный рендеринг, трудности с изменением компонентов и усложнение чтения кода. Лично я предпочитаю полностью автономные компоненты, извлекающие и отображающие свои данные без зависимости от своих родительских компонентов. Это может привести к некоторым недостаткам, таким как ненужные вызовы API (о которых мы поговорим позже), но это упрощает понимание ваших компонентов, их рефакторинг, тестирование и повторное использование, если это необходимо.
Чтобы решить проблему чрезмерного детализации реквизита, Redux предоставляет два полезных метода, которые можно использовать в сочетании с React: useSelector и useDispatch. Метод useSelector позволяет компонентам извлекать данные из хранилища Redux без необходимости сверления реквизитов, а метод useDispatch предоставляет способ отправки действий для обновления хранилища.
Для начала, первый шаг — определить, где в коде происходит сверление реквизита. Обычно это происходит, когда компонент получает свойство, которое он не использует сам, а вместо этого передает его дочерним компонентам. После идентификации свойство может быть заменено вызовом метода useSelector для извлечения данных непосредственно из хранилища. Это делает код более читабельным и легким для модификации, поскольку изменения в хранилище можно вносить централизованно, не затрагивая остальную часть кода.
Ниже приведен упрощенный пример того, как это можно сделать. Примечание. Редьюсеры и действия опущены для краткости.
// Before refactoring
const ParentComponent = ({ listProp, stringProp }) => {
return (
<div>
<ChildComponentOne listProp={listProp} />
<ChildComponentTwo stringProp={stringProp} />
</div>
);
};
// After refactoring ParentComponent
const ParentComponent = () => {
return (
<div>
<ChildComponentOne />
<ChildComponentTwo />
</div>
);
};
// After refactoring ChildComponentOne
ipmort { useEffect } from 'react';
import { useSelector, useDispatch } from 'react-redux';
import { fetchListAction } from 'MyStore/list
const ChildComponentOne = () => {
const dispatch = useDispatch();
const listProp = useSelector(state => state.someSlice.listProp);
useEffect(() => {
dispatch(fetchListAction)
}, [])
return (
<ul>
{listProp.map(item => <li key={item}>{item}</li>)}
</ul>
);
};
// After refactoring ChildComponentTwo
import { useSelector } from 'react-redux';
import { useSelector, useDispatch } from 'react-redux';
import { fetchStringAction } from 'MyStore/list
const ChildComponentOne = () => {
const dispatch = useDispatch();
const stringProp = useSelector(state => state.someSlice.stringProp);
useEffect(() => {
dispatch(fetchStringAction)
}, [])
return (
<div>
{stringProp}
</div>
);
};
В этом примере дочернему компоненту больше не нужно получать listProp и stringProp от своего родителя, вместо этого он извлекает эти реквизиты непосредственно из хранилища Redux с помощью метода useSelector. Дочерние компоненты также непосредственно отвечают за выборку данных.
Но что, если у вас есть несколько компонентов, которым необходимо отображать фрагменты данных, возвращаемых одним и тем же запросом API? Вы можете расширить свой вызов useEffect(), чтобы проверить статус вашего запроса API. В большой кодовой базе вы, вероятно, будете часто сталкиваться с этим и иметь много повторяющегося кода. Может, стоит сделать многоразовый крючок? Есть лучшее решение: Redux Toolkit Query (RTKQ).
Redux Toolkit Query — это мощная библиотека, которая упрощает процесс извлечения данных из API и управления состояниями загрузки и ошибок в магазине Redux. Хотя RTKQ может быть труден для понимания на начальном этапе — большая часть функций запутана — его использование может и, скорее всего, приведет к меньшему (и, следовательно, более простому в обслуживании) коду. Но самым большим преимуществом является то, что он может кэшировать ваши запросы, снижая нагрузку на ваш сервер API и ускоряя рендеринг ваших компонентов. Он предоставляет простой и декларативный API, который может значительно сократить объем шаблонного кода, необходимого для управления асинхронной выборкой данных в приложении Redux.
Чтобы использовать Redux Toolkit Query, сначала установите его как зависимость:
import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query';
const apiSlice = createApi({
reducerPath: 'api',
baseQuery: fetchBaseQuery({ baseUrl: 'https://api.example.com' }),
endpoints: builder => ({
getPosts: builder.query({
query: () => '/posts',
}),
}),
});
export const { useGetPostsQuery } = apiSlice;
В этом примере срез API определяется одной конечной точкой, которая извлекает список сообщений из API. Затем хук useGetPostsQuery можно использовать в компоненте React для извлечения данных и автоматического управления состояниями загрузки и ошибок.
Вот пример того, как это можно использовать в компоненте:
import { useGetPostsQuery } from './apiSlice';
const MyComponent = () => {
const { data, isLoading, isError } = useGetPostsQuery();
if (isLoading) {
return <div>Loading...</div>;
}
if (isError) {
return <div>Error loading posts</div>;
}
return (
<div>
{data.map(post => (
<div key={post.id}>{post.title}</div>
))}
</div>
);
};
Как вы можете видеть выше, использование RTKQ может значительно сократить объем написанного кода, удалив шаблоны, необходимые для написания действий и редукторов, которые включают случаи для обработки состояний загрузки и ошибок. Кэширование ответов значительно сократит время рендеринга ваших приложений и позволит компонентам повторно использовать одни и те же запросы без отправки множества ненужных запросов на ваш сервер.