По мотивам вопросов чатика react.js@telegram
Оффтоп: пожалуйста, не нужно в сотый раз объяснять уже набившую оскомину тему новичку, который задаст подобный вопрос. Просто поделитесь ссылкой на этот текст. С уважением, Андрей @XaveScor Звёздочка
Краткий ответ на этот вопрос, если вы не хотите разбираться детальнее:
- Если вы экспериментируете, то можете взять в качестве стейт-менеджера что угодно. Буквально. Опыт лишним не будет.
- Если вы ищете решение для проекта, на котором планируете зарабатывать деньги, то ни в коем случае не берите контекст в расчёт.
После стабилизации контекста и появления хуков в реакте появилась куча людей с одинаковым вопросом: можно ли контекстом заменить реатом/эффектор/редакст/мобыкс/ваш_любимый_стейт_менеджер? И если да, то стоит ли?
Ответ прост: да. Но возникает вопрос: стоит ли?
Давайте рассмотрим ситуацию, что у нас для работы со стейтом используется контекст и реакт дерево имеет примерно такую структуру:
/-----CHILD_1---------CHILD_1_1
BASE--- \--CHILD_1_2
\------CHILD_2
У компонентов CHILD_1_1 и CHILD_1_2 есть общий стейт, который мы храним в CHILD_1, и через контекст пробрасываем его в CHILD_1_1 и CHILD_1_2.
|Shared State here| |useContext here|
|-----------------| |---------------|
v v |
/-----CHILD_1---------CHILD_1_1 |
BASE--- \--CHILD_1_2 <---|
\------CHILD_2
И всё прекрасно. Мы не тащим лишних зависимостей и у нас всё работает. Но к нас внезапно приходит заказчик и говорит, что ему нужна новая фича. Суть этой фичи опустим, но для её реализации нужно иметь наш shared state
в CHILD_2. Что делать? Здесь всё просто:
- Ищем компонент
A
, в котором мы хранимshared state
. - CHILD_2 - это потомок
A
?- Да. Тогда у нас всё хорошо, просто используем useContext в CHILD_2 и радуемся жизни.
- Нет. Тогда боль:
0. Ищем общий предок
B
уA
и CHILD_2. 0. Переносим вB
всю логику работы сshared state
изA
. 0. ИспользуемuseContext
в CHILD_2.
Выглядит просто, не так ли? Возможно, что я уже вам доказал, что ваш_любимый_стейт_менеджер вам не нужен. Но подождите, давайте представим, что у вас не один shared state
, а 20; что у вас все shared state
хранятся не в одном компоненте, а в 10. В таких условиях схема выше для вас выглядит такой же простой? Для меня нет.
Давайте придумаем как нам писать приложение на основе контекста, чтобы с ним было проще работать.
Главная проблема при работе с контекстом - это поиск shared state
и общего предка. Как нам упростить эти шаги? Так как у нас реакт-дерево - это дерево, то у всех компонентов есть общий предок. Давайте хранить все shared state
в root'овом компоненте. И тогда у нас алгоритм проброса shared state
будет состоять только из одного пункта:
- Просто используем useContext в CHILD_2 и радуемся жизни.
Всё прекрасно, правда? Вроде как да. Теперь давайте рассмотрим как должен выглядеть shared state
для удобной работы с ним:
Благодаря редаксу в API реакта появился весьма удобный хук React.useReducer
, на основе которого можно построить shared state
:
const appReducer = (state, event) => {
return {
state1: reducer1(state.state1, event),
state2: reducer2(state.state2, event),
}
}
const AppStoreProvider = ({ children }) => {
const [state, dispatch] = React.useReducer(appReducer, initialState);
return(
<AppDispatchContext.Provider value={dispatch}>
<AppStateContext.Provider value={state}>
{children}
</AppStateContext.Provider>
</AppDispatchContext.Provider>
)
}
Выглядит весьма неплохо, так как мы очень легко расширяем логику путём добавления редьюсеров в appReducer
. С этим уже можно жить, если не задумываться о:
- Наше решение очень-очень похоже на редакс. Почему бы не взять его?
- В редаксе есть оптимизации, которые позволяют ему работать куда лучше, к примеру combineReducers.ts#L192 - не пересоздаём те части стейта, которые не были изменены
- У биндингов к реакту
react-redux
есть уже готовые хуки по типуuseSelector
, которые не ререндерят компонент, если стейт не сохранился. - У
react-redux
есть какие-никакие девтулзы, которые позволяют дебажить приложение.
Беря во внимание как минимум эти 4 пункта я не вижу смысла советовать пились собственное редаксоподобное решение, которое будет ничем не лучше, чем сам редакс во всём, кроме одного пункта: суммарный размер вашего решения будет меньше чем у редакса - это правда. Но в остальном полные минусы, так что не нужно изобретать то, что уже изобретено. У вас стоит задача написать приложение, а не написать лучший стейт менеджер.
У вас стоит задача написать приложение, а не написать лучший стейт менеджер. В ходе разработки своего приложения вы неизбежно придёте к тому, что изобретёте собственный стейт менеджер.
Если у вас маленький проект, то стоит взять что-то попроще, к примеру, Svelte. Так вы получите очень маленький размер бандла.
А возможно у вас вообще нет динамики, поэтому лучше просто нагенерить статических страниц с помощью Hugo
Если у вас большой проект, то за небольшой размер зависимостей вы отплатите увеличившимся размером проекта. В итоге поменяете шило на мыло.