Skip to content

Instantly share code, notes, and snippets.

@XaveScor
Last active February 2, 2024 10:57
Show Gist options
  • Save XaveScor/99431c573b53b8a0c41fb3b5fec522bc to your computer and use it in GitHub Desktop.
Save XaveScor/99431c573b53b8a0c41fb3b5fec522bc to your computer and use it in GitHub Desktop.
Context vs StateManager

По мотивам вопросов чатика 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. Что делать? Здесь всё просто:

  1. Ищем компонент A, в котором мы храним shared state.
  2. 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 будет состоять только из одного пункта:

  1. Просто используем 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. С этим уже можно жить, если не задумываться о:

  1. Наше решение очень-очень похоже на редакс. Почему бы не взять его?
  2. В редаксе есть оптимизации, которые позволяют ему работать куда лучше, к примеру combineReducers.ts#L192 - не пересоздаём те части стейта, которые не были изменены
  3. У биндингов к реакту react-redux есть уже готовые хуки по типу useSelector, которые не ререндерят компонент, если стейт не сохранился.
  4. У react-redux есть какие-никакие девтулзы, которые позволяют дебажить приложение.

Беря во внимание как минимум эти 4 пункта я не вижу смысла советовать пились собственное редаксоподобное решение, которое будет ничем не лучше, чем сам редакс во всём, кроме одного пункта: суммарный размер вашего решения будет меньше чем у редакса - это правда. Но в остальном полные минусы, так что не нужно изобретать то, что уже изобретено. У вас стоит задача написать приложение, а не написать лучший стейт менеджер.

Вывод

Не нужно использовать контекст как стейт менеджер

У вас стоит задача написать приложение, а не написать лучший стейт менеджер. В ходе разработки своего приложения вы неизбежно придёте к тому, что изобретёте собственный стейт менеджер.

Вы хотите уменьшить размер зависимостей?

Если у вас маленький проект, то стоит взять что-то попроще, к примеру, Svelte. Так вы получите очень маленький размер бандла.

А возможно у вас вообще нет динамики, поэтому лучше просто нагенерить статических страниц с помощью Hugo

Если у вас большой проект, то за небольшой размер зависимостей вы отплатите увеличившимся размером проекта. В итоге поменяете шило на мыло.

@dmitryshelomanov
Copy link

и да посоны
берем эффектор и понимаем что редакс и контексты не нужны)

@budarin
Copy link

budarin commented Oct 22, 2020

  • да не согласен я с ним!
  • с кем?
  • да с обоими!
    :)

веселопеды нужны!
веселопеды важны!
без них нет движения вперед :)

https://www.npmjs.com/package/@budarin/use-react-redux

использую на проекте и не страдаю - все фишки redux включая производительность кроме devtools (я ими и в редаксе не особенно пользовался)

@carina-akaia
Copy link

Но к нас внезапно приходит заказчик и говорит, что ему нужна новая фича.

опечаточка

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment