React State Management в 2021 году

внешний интерфейс React.js
React State Management в 2021 году

предисловие

Как мы все знаем, React — это библиотека, ориентированная на слой пользовательского интерфейса, отличная от фреймворка Vue, Angular и других фреймворков, а различные программы управления статусом React — все в сотне цветов/групп. В дополнение к популярным библиотекам Redux, Mobx, Recoil, Zustand, официальная версия React также достигла 17, readystate, userducer, usecontext и другие концепции и приложения, связанные с управлением статусом, постепенно укореняются.

2021 год снова подходит к концу, и пришло время подвести итоги последних решений по управлению состоянием в сообществе React.

Принцип здесь не является подробным, а представлены только характеристики и преимущества и недостатки каждой схемы.

Реагировать на управление состоянием

В React есть три основных категории управления состоянием:Local state、Context、第三方库.

Среди них загрузки npm и хранилища Github популярных сторонних библиотек за последние два года:

Npm Downloads

Stats

(Видеть:npm trends)

Видно, что старший брат редукс и его младшие братья (thunk, saga, observable) по-прежнему сильны, mobx прохладен, а отдача восходящей звезды лютая, всего через год после релиза у него 14 тысяч звезд.

Local State

После React v16.8 функциональный компонент стал мейнстримом, а управление локальным состояниемuseStateа такжеuseReducerмир закончился.

useState — более детальное состояние

В отличие от Class Component, который помещает все состояния в один объект,useStateИдея состоит в том, чтобы разделить состояние внутри компонента и поддерживать его с более высокой степенью детализации:

import {useCallback, useState} from 'react';

const Foo = () => {
  const [stateA, setStateA] = useState(0);
  const [stateB, setStateB] = useState(0);

  const handleAdd = useCallback(
    () => { setStateA(prev => prev + 1); },
    []
  );
  const handleSubtract = useCallback(
    () => { setStateB(prev => prev - 1); },
    []
  );

  return (
    <>
      <Button onClick={handleAdd}>{stateA}</Button>
      <Button onClick={handleSubtract}>{stateB}</Button>
    </>
  );
};

useReducer — сложная логическая абстракция и повторное использование

Студенты, знакомые с Redux, могут легко понятьuseReducer. Использование useReducer можно рассматривать как создание независимого редукционного хранилища внутри компонента, а логику этого редуктора можно повторно использовать в разных компонентах.

Когда логика вычисления состояния сложна, или изменения производного состояния имеют общие черты, или логика редуктора может использоваться повторно, сначала можно использовать useReducer.

Например, инкапсуляция общей логики подкачки списка:

import {useReducer} from 'react';

const initialState = { pageNum: 1, pageSize: 15 };

const reducer = (state, action) => {
  switch (action.type) {
    case 'next':  // 下一页
      return { ...state, pageNum: state.pageNum + 1 };
    case 'prev':  // 前一页
      return { ...state, pageNum: state.pageNum - 1 };
    case 'changePage':  // 跳转到某页
      return { ...state, pageNum: action.payload };
    case 'changeSize':  // 更改每页展示条目数
      return { pageNum: 1, pageSize: action.payload };
    default:
      return state;
  }
};

const Page = () => {
  const [pager, dispatch] = useReducer(reducer, initialState);

  return (
    <Table
      pageNum={pager.pageNum}
      pageSize={pager.pageSize}
      onGoNext={() => dispatch({ type: 'next' })}
      onGoPrev={() => dispatch({ type: 'prev' })}
      onPageNumChange={(num) => dispatch({ type: 'changePage', payload: num })}
      onPageSizeChange={(size) => dispatch({ type: 'changeSize', payload: size })}
    />
  );
};

Еще одним преимуществом использования USERDUCER является оптимизация производительности приложений, когда глубокие подкомпоненты должны вызвать обновления.

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

Когда уровень компонента особенно глубок и коллбэков слишком много, вы вспомните страх перед доминированием прозрачной передачи пропса, и тот тип пропса, который прописан в подкомпонентах, нужно отклеить. И метод, инкапсулированный useCallback, может обновлять зависимые переменные и возвращать новые ссылки, что может привести к тому, что подкомпоненты прозрачного пути передачи инициируют обновление.

Если вы используете useReducer, вы можете комбинироватьuseContext, передавать диспетчеризацию только дочерним компонентам. И после того, как сгенерируется диспетчеризация, ссылка постоянна, и возможное принудительное обновление контекста не сработает.

import {createContext, useReducer, useContext} from 'react';

const ParentDispatch = createContext(null);

const Parent = () => {
  const [state, dispatch] = useReducer(reducer, initialState);

  return (
    <ParentDispatch.Provider value={dispatch}>
      <DeepTree parentState={state} />
    </ParentDispatch.Provider>
  );
};

// 深层子组件
const DeepChild = () => {
  const dispatch = useContext(ParentDispatch);

  const handleClick = () => {
    dispatch({ type: 'add', payload: 'hello' });
  };

  return <button onClick={handleClick}>Add</button>;
};

смотрите подробностиHow to avoid passing callbacks down?.

Context

В типичном приложении React данные передаются сверху вниз (от родителя к дочернему) через свойство props, а контекст предоставляет API для передачи реквизитов по иерархии компонентов. Контекст React не нов, поэтому я не буду вдаваться в подробности его концепции и использования.

useContext

Комбинированный в ФКcreateContextа такжеuseContextИспользуйте, см. пример в useReducer выше.

Проблемы с контекстом

Проблемы с Context также являются обычным явлением.

В реакции контекст является анти-шаблоном, который отличается от мелкозернистых реактивных обновлений, таких как избыточность, Как только значение контекста изменится, все компоненты, которые зависят от контекста, будут обновлены.force update, потому что контекстный API не может провести детальный анализ того, от каких свойств в контексте зависит компонент, и может проникнутьReact.memoа такжеshouldComponentUpdateДля сравнения, все задействованные компоненты принудительно обновляются.

Официальная документация React находится по адресуWhen to Use ContextРаздел гласит:

Контекст предназначен для совместного использования данных, которые являются «глобальными» для дерева компонентов, например, текущий аутентифицированный пользователь, тема или предпочитаемый язык.

Подводя итог, контекст следует использовать с осторожностью при совместном использовании связанных с бизнесом и часто меняющихся данных в системе.

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

Кроме того, в официальной документации также упоминаетсяеще одна дырка, вы также должны обратить внимание при его использовании.

Библиотека государственного управления

Самыми популярными библиотеками управления состоянием React на сегодняшний день являются Redux, Mobx и Recoil, среди них Redux и Mobx — представители старых и мощных игроков, а Recoil — самая горячая восходящая звезда за последние два года.

Redux

Redux должен быть знаком каждому:

Запущен React-ReduxuseDispatch,useSelectorПосле ожидания хука сокращается много кода, который раньше использовал высокоуровневые компоненты (контейнер) для подключения хранилища и представления, что значительно снижает стоимость получения состояния и инкапсуляции действий, а использование становится более гибким.

Redux сам по себе очень чистый, и мысленная модель не сложна, но фактическое использование должно соответствоватьredux-thunk、redux-saga、redux-observableЭти промежуточное ПО (middleware) иreselect、immerТакие вспомогательные инструменты могут достичь действительно полезного состояния.Повышая стоимость обучения, промежуточное ПО также будет вводить различные побочные эффекты и промежуточные состояния.Поток реального состояния не так красив, как должен быть.

Самая большая критика Redux заключается в том, что в нем слишком много повторяющегося кода шаблона, но команда Redux не игнорирует это. Дэн Абрамов (автор редукса) много раз подчеркивал в Твиттере, что редукс разработан, чтобы служить следующим принципам:要让状态的变化可追踪,可重复,可维护, поэтому есть понятия редюсер, действие, промежуточное ПО. Чтобы реализовать простую операцию обновления состояния, необходимо изменить пять или шесть файлов, чтобы написать полный набор кода шаблона, Это пустая трата средств или цена ремонтопригодности, что является вопросом мнения.

Шаблон код гораздо больше, использоватьRedux ToolkitНекоторые улучшения могут быть сделаны.Он инкапсулирует метод написания редюсера и действия и поставляется с некоторыми полезными наборами инструментов (но стоимость обучения, похоже, снова возрастает).

{
  "dependencies": {
    "immer": "^9.0.6",
    "redux": "^4.1.0",
    "redux-thunk": "^2.3.0",
    "reselect": "^4.0.0"
  },
}

Redux Middleware

Принцип мидлвара Redux аналогичен: благодаря предварительной обработке мидлвара он позволяет выполнять более гибкие действия (которые могут быть функциями или промисами и т. д.) диспетчеризации во View (компоненте), а затем обрабатывать различные побочные эффекты (запросы интерфейса и т. д.). ) в промежуточном программном обеспечении; или это встроенный пользовательский конечный автомат (redux-saga) и т. д. Но, в конце концов, все это нужно для того, чтобы преобразовать действие в сокращение.plain objectформат, отправка в хранилище редуктов.

Давайте взглянем на трех гигантов промежуточного ПО Redux.redux-thunk,redux-saga,redux-observableДва годаТенденции использования:

Npm Downloads

Stats

Redux-thunk по-прежнему используется наиболее часто, и он неуклонно растет, постепенно увеличивая разрыв с другим промежуточным программным обеспечением; использование redux-saga почти удвоилось за последние два года. Последние версии Thunk и saga были два-три года назад и достигли относительно стабильного состояния. Redux-observable все еще обновляется, но рост использования, похоже, остановился.

Redux-thunk

Redux-thunk позволяет приложениям отправлять функцию в компоненте (эта функция называется thunk).Асинхронный код, исходный в компоненте, извлекается и реализуется в этом thunk, чтобы его можно было повторно использовать в разных компонентах.

Thunkэто псевдоним для функции, основной целью которой является задержка выполнения задачи или добавление некоторых дополнительных операций до и после выполнения другой функции. Для подробного ознакомления с режимом Thunk см.What the heck is a 'thunk'?(илиперевод).

Следующий собственный метод написания редукса:

// 原生Redux用法
import { useCallback, useEffect } from 'react';
import { useDispatch, useSelector } from 'react-redux';

const Demo = () => {
  const dispatch = useDispatch();

  const fetchUser = useCallback(
    async () => {
      const result = await getUserApi(params);
      dispatch({
        type: 'RECEIVE_USER_INFO',
        payload: result,
      });
    },
    [dispatch]
  );

  useEffect(
    () => {
      fetchUser();
    },
    [fetchUser]
  );
  
  const currentUser = useSelector(state => state?.context?.currentUser);

  return <div>{currentUser?.name}</div>;
};

Используйте Redux-thunk, чтобы изменить приведенный выше пример:

// Redux Thunk Creator
const fetchUser = (params) => {
  return async (dispatch, getState) => { // This is a Thunk
    const result = await getUserApi(params);
    dispatch({
      type: 'RECEIVE_USER_INFO',
      payload: result,
    });
  };
}

const Demo = () => {
  const dispatch = useDispatch();

  useEffect(
    () => {
      dispatch(fetchUser(params));
    },
    [dispatch]
  );

  const currentUser = useSelector(state => state?.context?.currentUser);

  return <div>{currentUser?.name}</div>;
};

Приведенный выше код выглядит знакомым? Дизайн Redux-thunk на самом деле очень продвинутый.Когда шесть лет назад высокоуровневые компоненты бегали по всей улице, идея redux-thunk была точно такой же, как сегодняшние хуки.

Мы преобразуем приведенный выше код с помощью хука + нативного редукса:

const useFetchUser = () => {
  const dispatch = useDispatch();

  const fetchUser = useCallback(
    async (params) => {
      const result = await getUserApi(params);
      dispatch({
        type: 'RECEIVE_USER_INFO',
        payload: result,
      });
    },
    [dispatch]
  );

  return fetchUser;
};

const Demo = () => {
  const fetchUser = useFetchUser();

  useEffect(
    () => {
      fetchUser(params);
    },
    [fetchUser]
  );

  const currentUser = useSelector(state => state?.context?.currentUser);

  return <div>{currentUser?.name}</div>;
};

Redux-thunk написан Дэном Абрамовым, автором redux,исходный кодВсего 14 строк, просто секси, и вы можете освоить промежуточное ПО Redux за одну минуту.

Лично я считаю, что с хуками redux-thunk, возможно, выполнил свою историческую миссию, ведь он может делать все хуки, на которые способен.

Redux-saga

Redux-saga стремится сделать побочные эффекты в приложениях более управляемыми на основеОсобенности генератора ESвыполнить. Думайте о саге как об отдельном потоке в приложении, которое несет исключительную ответственность за обработку побочных эффектов.

Saga тщательно инкапсулирует управление побочными эффектами. Наша диспетчеризация в компоненте все еще избыточна原生action(plain object), действие отслеживается в саге, и выполняется обратный вызов, соответствующий типу действия. Этот способ написания эквивалентен извлечению асинхронного кода из компонента в сагу для управления.

Одним из больших преимуществ Saga является встроенныйrace,takeLatest,takeEveryи другие стратегии, вы можете легко реализовать действие竞态(Racing Effects)和并发(Concurrency)Поддержка сценария.

В то же время, благодаря высокой степени инкапсуляции, saga имеет множество концепций и интерфейсов, в том числе:

  • Взаимодействие с компонентами и редуксом (взять, выбрать, поставить)
  • Блокирующие и неблокирующие звонки (fork, call)
  • Гонка, параллелизм (race, takeLatest, takeEvery)
  • ...

Давайте используем saga для реализации приведенного выше примера thunk:

// Demo.jsx
const Demo = () => {
  const dispatch = useDispatch();

  useEffect(
    () => {
      dispatch({ type: 'FETCH_USER_INFO', payload: {} });  // dispatch原生action
    },
    [dispatch]
  );

  const currentUser = useSelector(state => state?.context?.currentUser);

  return <div>{currentUser?.name}</div>;
};

// sagas.js
import { call, put, takeEvery } from 'redux-saga/effects';

function *fetchUser(action) {
  const result = yield call(getUserApi, action.payload);  // 发起请求
  
  yield put({  // 真正dispatch action到redux store
    type: 'RECEIVE_USER_INFO',
    payload: result,
  });
}

function *mySaga() {
  yield takeEvery('FETCH_USER_INFO', fetchUser);  // 监听每一个 FETCH_USER_INFO ,并调用 fetchUser
}

export default mySaga;

В приведенном выше примере действие, отслеживаемое сагой,FETCH_USER_INFO, окончательное действие отправки в хранилище избыточностиRECEIVE_USER_INFO, что полностью отражает концепцию саги: просмотр нужно только активировать, а сохранить нужно только обновить Код асинхронного/побочного эффекта в середине обрабатывается сагой.

Но после этого звони, ставь, и бери, спрошу, не запутался ли новенький. Кроме того, сага также будет писать много шаблонного кода, вкупе с высокой повторяемостью самого редукса, можно сказать, еще хуже для разработчиков.

Рассмотрим пример блокировки вызова асинхронного интерфейса официального сайта и поддержки отмены на полпути:

import { take, put, call, fork, cancel, cancelled, delay } from 'redux-saga/effects'
import { someApi, actions } from 'somewhere'

function* bgSync() {
  try {
    while (true) {
      yield put(actions.requestStart())
      const result = yield call(someApi)
      yield put(actions.requestSuccess(result))
      yield delay(5000)
    }
  } finally {
    if (yield cancelled())
      yield put(actions.requestFailure('Sync cancelled!'))
  }
}

function* main() {
  while ( yield take('START_BACKGROUND_SYNC') ) {
    // starts the task in the background
    const bgSyncTask = yield fork(bgSync)

    // wait for the user stop action
    yield take('STOP_BACKGROUND_SYNC')
    // user clicked stop. cancel the background task
    // this will cause the forked bgSync task to jump into its finally block
    yield cancel(bgSyncTask)
  }
}

Просто Redux Pro Max 1T.

Преимущество Saga в том, что она лучше поддерживает конкуренцию и параллелизм. Студентам, которые не сталкивались с ней, просто нужно помнить об этом моменте. Когда есть сценарий спроса, еще не поздно посмотреть документацию и подумать об использовании Ведь связана еще одна эпопея, Middleware не конфликтует с нативным redux и thunk.

Mobx — Написание Vue в React

Как и redux, mobx сама по себе является независимой от пользовательского интерфейса библиотекой управления чистым состоянием.mobx-reactили зажигалкаmobx-react-liteПодключиться к реакции.

пример

Давайте сначала рассмотрим простой пример реализации счетчика с помощью mobx:

import { useEffect } from 'react'
import { autorun } from 'mobx';
import { Observer, useLocalObservable } from 'mobx-react-lite';

const Counter = () => {
    const counterStore = useLocalObservable(() => ({  // 创建一个局部的observable state,生命周期和组件一致
        value: 0,
        get doubleValue() {  // computed value
            return counterStore.value * 2;
        },
        increment() {
            counterStore.value += 1;
        },
        decrement() {
            counterStore.value -= 1;
        },
    }));

    useEffect(
        () => {
            autorun(() => {  // 收集依赖,并在依赖项变化时重新执行
                console.log(counterStore.value);
            });
        },
        []
    );

    return (
        /* Observer组件会收集dom节点对store的依赖项,并使渲染变成响应式 */
        <Observer>
            {() => (
                <div>
                    <div>Value: {counterStore.value}</div>
                    <div>Double value: {counterStore.doubleValue}</div>
                    <button onClick={counterStore.increment}>Add</button>
                    <button onClick={counterStore.decrement}>Decrement</button>
                </div>
            )}
        </Observer>
    );
};

В примере состояние также может быть извлечено как глобальное:

import { useEffect } from 'react'
import { autorun, makeAutoObservable } from 'mobx';
import { useObserver } from 'mobx-react-lite';

// 全局state,可以在多个组件中订阅
const counterStore = makeAutoObservable({  // 把对象变成observable
    value: 2,
    get doubleValue() { // computed value
        return counterStore.value * 2;
    },
    increment() {
        counterStore.value += 1;
    },
    decrement() {
        counterStore.value -= 1;
    },
});

const Counter = () => {
    return (
        /* useObserver 的作用和 <Observer> 一样 */
        useObserver(() => (
            <div>
                <div>Value: {counterStore.value}</div>
                <div>Double value: {counterStore.doubleValue}</div>
                <button onClick={counterStore.increment}>Add</button>
                <button onClick={counterStore.decrement}>Decrement</button>
            </div>
        ))
    );
};

ментальная модель

Ментальная модель Mobx очень похожа на react, в ней различаются три концепции применения:

  • Состояние
  • Действия
  • Производные

Сначала создайте наблюдаемое состояние (Observable State), обновите State через Action, а затем автоматически обновите все производные (Derivations). Производные включают вычисляемое значение (аналогично useMemo или useSelector), функции побочных эффектов (аналогично useEffect) и пользовательский интерфейс (рендеринг).

Хотя ментальная модель Mobx похожа на реакцию, реализация совершенно другая:mutable + proxy(Для совместимости прокси фактически реализован с использованием Object.defineProperty).

Использование шаблона потока данных, направленного против реагирования, обязательно будет иметь свою стоимость:

  • Отзывчивость Mobx не входит в жизненный цикл самой реакции, поэтому она должна явно объявлять время и объем своего происхождения. Например, для срабатывания побочного эффекта нужно запустить автозапуск/реакцию в useEffect, а для DOM-рендеринга нужно обернуть слой useObserver/Observer, что удорожает разработку.

  • Mobx будет собирать зависимости при монтировании компонента и устанавливать связь с состоянием.Этот метод может не мигрировать плавно в грядущем параллельном режиме реакции 18. Для этого был специально разработан reactcreate-subscriptionЭтот метод используется для подписки на внешние источники в компоненте, но фактический эффект приложения неизвестен.

Сущность я также покрыл главы: ract + mobx - это, по сути, более громоздкий Vue.

В этом случае не стоит использовать Vue напрямую (собачья голова).

Mobx vs Redux

Сравнение между Mobx и Redux можно свести к следующему:面向对象 vs 函数式а такжеMutable vs Immutable.

  • По сравнению с отправкой обхода широковещательной рассылки Redux, а затем обходом и оценкой ссылок для определения того, обновлены ли компоненты, mobx может точно собирать зависимости и локально обновлять компоненты (аналогично vue) на основе прокси, что теоретически будет иметь лучшую производительность, но Redux считает, что это может не будет одного вопроса(Не будет ли вызов «всех моих редукторов» для каждого действия медленным?)

  • MOBX, потому что данные являются только ссылкой, нет возможности возврата, в отличие от Redux каждое обновление, эквивалентное воспроизведение снимка, с ввод в эксплуатациюredux-loggerТакое промежуточное ПО может интуитивно видеть историю изменений потока данных.

  • Стоимость обучения Mobx ниже, и нет семейного сегмента.

  • Mobx удобнее при обновлении глубоко вложенных свойств в состоянии, просто присваивайте значения напрямую, тогда как redux нужно обновлять все ссылки, проходящие через иерархию (конечно сimmerнет проблемы).

Recoil

RecoilЭто библиотека управления состоянием, официально запущенная facebook для реакции на конференции React Europe 2020. Мотивация состоит в том, чтобы устранить ограничения режима обмена состоянием реакции:

  • В прошлом было возможно только продвигать состояние к общему предку для достижения совместного использования состояния, и как только это было сделано, было практически невозможно разделить код на верхний уровень дерева компонентов (где должно существовать состояние) и конечные компоненты. (где используется состояние)
  • Контекст может хранить только одно значение, он не может хранить несколько коллекций значений, у каждого из которых есть потребители.

Откат имеет следующие особенности:

  • Атомизация состояния (атом), свободная композиция и подписка, а определение состояния является прогрессивным и распределенным, что позволяет разделить код.
  • Кода шаблона нет, это естественно режим хука, так что react максимально сохраняет первоначальный вид
  • Совместимость с параллельным режимом (Concurrent Mode)
  • Обеспечивает поддержку моментальных снимков для потока состояния, который может легко отслеживать состояние приложения и даже кодировать моментальный снимок в URL-адрес, чтобы любой, кто открывает приложение, мог войти в то же состояние.

Recoil имеет много отличных функций и является достойной внимания библиотекой, но она все еще находится в экспериментальной стадии, версия выпущена только до 0.4.1 (по состоянию на 13 октября 2021 г.), и существует большое количество проблем. должны быть разрешены, которые следует использовать с осторожностью в официальных проектах.

пример

Реализовать отфильтрованный список:

Исходный код:

import {atom, selector, useRecoilState, useRecoilValue} from 'recoil';

const listState = atom({  // 列表
    key: 'listState',
    default: [
        {name: 'Tom', sex: 'male'},
        {name: 'Allen', sex: 'male'},
        {name: 'Lucy', sex: 'female'},
    ],
});

const filterState = atom({  // 筛选项的值
    key: 'filterState',
    default: 'all',
});

const filteredListState = selector({ // 筛选后的列表(selector定义派生状态)
    key: 'filteredListState',
    get: ({get}) => {
        const list = get(listState);  // 通过get方法获取其他state的值
        const filter = get(filterState);
        
        return filter === 'all' ? list : list.filter(item => item.sex === filter);
    },
});

const List = () => {
    const [filter, setFilter] = useRecoilState(filterState);
    const filteredList = useRecoilValue(filteredListState);

    return (
        <div>
            <Radio.Group value={filter} onChange={e => { setFilter(e.target.value) }}>
                <Radio value="all">all</Radio>
                <Radio value="male">male</Radio>
                <Radio value="female">female</Radio>
            </Radio.Group>
            <ul className="list">
                {filteredList.map(item => (
                    <li key={item.name}>{item.name}</li>
                ))}
            </ul>
        </div>
    );
};

Два основных метода определения состояния в Recoil:

  • atom: определить атомарное состояние, то есть минимальный набор определенного состояния компонента,
  • selector: определить производное состояние, которое фактически является вычисленным значением.

Методы состояния потребленияuseRecoilState,useRecoilValue,useSetRecoilStateИ т. д., использование аналогично useState реакции, и для начала почти нет затрат. Также стоит отметить, что recoil в настоящее время поддерживает только использование хука FC.Если компонент класса хочет его использовать, он может получить состояние через HOC и внедрить его в компонент.

ментальная модель

Набор состояний Recoil представляет собой ориентированный граф, ортогональный и естественным образом связанный с деревом компонентов React. Изменения состояния начинаются в вершинах графа (атомах), проходят через чистые функции (селекторы) и затем переходят в компоненты.

Snapshot

Каждое изменение состояния Recoil создает неизменяемый снимок.Используя эту функцию, вы можете быстро реализовать функции, связанные с навигацией по приложению, такие как возврат состояния, переход и т. д.

Давайте посмотрим на расширение со списком фильтров в приведенном выше примере: добавьте кнопку и при ее нажатии сгенерируйте URL-адрес, содержащий информацию о моментальном снимке состояния страницы, и другие люди также смогут загружать ту же страницу статуса при доступе к этому URL-адресу.

import {useRecoilSnapshot, useGotoRecoilSnapshot} from 'recoil';

const SharedList = () => {
    const snapshot = useRecoilSnapshot();  // 获取当前全局状态的snapshot,每次变化都会更新
    const gotoSnapshot = useGotoRecoilSnapshot();

    const handleGenerateUrl = () => {
        const url = mapSnapshotToUrl(snapshot);  // 将snapshot信息编码进url
        console.log(url);
    };

    useEffect(
        () => {
            // 组件加载时从url中获取snapshot信息并跳转状态
            const snapshot = getSnapshotFromUrl();
            snapshot && gotoSnapshot(snapshot);
        },
        []
    );

    return (
        <>
            <List />
            <button onClick={handleGenerateUrl}>Generate URL</button>
        </>
    );
};

Примечание: mapSnapshotToUrl — это настраиваемый метод кодирования состояния, и в будущем recoil предоставит официальную вспомогательную реализацию.

Суммировать

Спасибо, что посмотрели это, я не знаю, с каким планом вы согласны сейчас? Можно сделать вывод, что серебряной пули для управления состоянием реакции на сегодняшний день все еще нет, нет лучшего, есть только самое подходящее.

Простые сцены используют нативныеuseState、useReducer、useContextможет быть удовлетворен; также может быть использованhoxТакая маленькая и красивая библиотека напрямую расширяет состояние хука до постоянного состояния, почти без дополнительной умственной нагрузки.

Для применения сложных сценариев redux и mobx — это библиотеки, прошедшие тысячи испытаний, и экология сообщества также очень полная.

Redux имеет множество шаблонов и уровней, с четким разделением обязанностей, что формирует его преимущества с точки зрения отслеживаемости и удобства обслуживания; он почти всемогущ с промежуточным программным обеспечением, таким как thunk и saga.

Преимущество Mobx в том, что он прост в написании и обладает высокой производительностью, но ремонтопригодность состояния не так хороша, как у redux, и его совместимость в параллельном режиме еще предстоит выяснить.

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

При остроте реакции может не наступить и дня единого управления состоянием (саморазработка UI-библиотеки), а все, что не имеет к себе никакого отношения, будет передано в комьюнити, постепенно сходится в практике разработки, и затем новое и старое в версии реагировать.изменить.

С появлением хуков и официальных откатов государственное управление, похоже, движется в сторону原子化、组件化Направление развития, которое также соответствует философии компонентизации реакции. Обход и распределение грубой силы Redux, возможно, были встречным решением.

Наконец, прилагается многогранное сравнение основных решений:

строить планы стоимость обучения стоимость кодирования ТС дружелюбный SSR Code Split Совместимость с параллельным режимом отлаживаемость Экологическое процветание
Redux высоко высоко в общем служба поддержки не поддерживается служба поддержки Хорошо высоко
Mobx середина середина Хорошо служба поддержки служба поддержки неизвестный Разница середина
Recoil Низкий Низкий Хорошо меньше практики служба поддержки служба поддержки Хорошо Низкий

над.