Библиотека управления состоянием React нового поколения от Facebook Recoil

JavaScript React.js

существуетReact Europe 2020 Conferenceначальство,FacebookпрограммистDave McCabeПредставлена ​​новая библиотека управления состояниемRecoil.

RecoilЭто все еще в экспериментальной стадии, это уже вFacebookНекоторые внутренние продукты используются в производственной среде. Ведь это официальная структура государственного управления, раньше у меня не было времени ее внимательно изучить, я ознакомился с ней во время Национального дня и поделился со всеми.

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

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

Наиболее распространенная практика заключается в том, чтобы поставитьstateРаспространяется родительским компонентом наListиCanvasДва компонента, очевидно, так каждый разstateПосле изменения все узлы будут полностью обновлены.

Конечно, мы также можем использоватьContext API, мы сохраняем состояние узла вContextвнутри, покаProviderсерединаpropsпроизошло изменения,ProviderВсе потомки-потребители перерисовываются.

Чтобы избежать проблемы полного рендеринга, мы можем хранить каждый дочерний узел в отдельномContext, так что каждый дополнительный узел будет добавлять слойProvider.

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

Ввести отдачу

Recoilсам должен решитьReactПроблема управления глобальным потоком данных использует шаблон проектирования децентрализованного управления атомарным состоянием.

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

ты можешь поставитьAtomпредставить как группуstateсбор, изменениеAtomБудут визуализированы только определенные дочерние компоненты, а весь родительский компонент не будет повторно визуализирован.

Разве вы не можете использовать Redux или Mobx?

так какReactпредоставлено само собойstateСостояние очень сложно разделить между компонентами, поэтому мы обычно используем некоторые другие библиотеки, такие какRedux、Mobxчтобы помочь нам управлять состоянием. Эти библиотеки в настоящее время широко используются, и мы не сталкивались с какими-либо серьезными проблемами, поэтомуFacebookЗачем внедрять новую структуру управления состоянием?

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

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

Основное использование

инициализация

использоватьrecoilКомпоненты с состоянием должны использоватьRecoilRootЗаверните:

import React from 'react';
import {
  RecoilRoot,
  atom,
  selector,
  useRecoilState,
  useRecoilValue,
  useSetRecoilState
} from 'recoil';

function App() {
  return (
    <RecoilRoot>
      <CharacterCounter />
    </RecoilRoot>
  );
}

определить состояние

Мы упоминали вышеAtomКонцепция чего-либо,Atomновое государство, но с традиционнымstateдругой, на него может подписаться любой компонент, когдаAtomПри обновлении каждый подписанный компонент повторно отображается с новым значением.

Сначала давайте определимAtom:

export const nameState = atom({
  key: 'nameState',
  default: 'ConardLi'
});

Таким образом, вам не нужно делать что-то вродеReduxОпределите состояние централизованно таким образом, вы можете сделать это какMobxТочно так же данные разбросаны и определены где угодно.

создатьAtom, должен предоставитьkey, который должен быть вRecoilRootОн уникален в пределах области действия, и для предоставления значения по умолчанию значением по умолчанию может быть статическое значение, функция или даже асинхронная функция.

Статус подписки и обновления

RecoilиспользоватьHooksДля подписки и обновления статуса обычно используются следующие три API:

  • useRecoilState: один как useStateHook, вы можете получитьatomстоимость иsetterписьмо
  • useSetRecoilState: получить толькоsetterфункция, если используется только эта функция, изменения состояния не приведут к повторному рендерингу компонента
  • useRecoilValue: получить только статус
import { nameState } from './store'
// useRecoilState
const NameInput = () => {
  const [name, setName] = useRecoilState(nameState);
  const onChange = (event) => {
   setName(event.target.value);
  };
  return <>
   <input type="text" value={name} onChange={onChange} />
   <div>Name: {name}</div>
  </>;
}

// useRecoilValue
const SomeOtherComponentWithName = () => {
  const name = useRecoilValue(nameState);
  return <div>{name}</div>;
}

// useSetRecoilState  
const SomeOtherComponentThatSetsName = () => {
  const setName = useSetRecoilState(nameState);
  return <button onClick={() => setName('Jon Doe')}>Set Name</button>;
}

Производное состояние

selectorПредставляет часть производного состояния, которое позволяет нам строить зависимости от другихatomстатус. он имеет обязательныйgetфункция, которая работает сreduxизreselectилиMobXиз@computedпохожий.

const lengthState = selector({
  key: 'lengthState', 
  get: ({get}) => {
    const text = get(nameState);
    return text.length;
  },
});

function NameLength() {
  const length = useRecoilValue(charLengthState);
  return <>Name Length: {length}</>;
}

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

Асинхронное состояние

RecoilОбеспечивает сопоставление состояний и производных состояний сReactКомпонентный метод. Действительно мощная функция заключается в том, что функции в графе также могут быть асинхронными. Это позволяет нам асинхронноReactЛегко используйте асинхронные функции в функциях рендеринга компонентов. использоватьRecoil, вы можете плавно смешивать синхронные и асинхронные функции в графе потока данных селектора. только из селектораgetвернуться в обратном вызовеPromise, а не само возвращаемое значение.

Например, в следующем примере, если имя пользователя хранится в какой-либо базе данных, которую нам нужно запросить, тогда все, что нам нужно сделать, это вернутьPromiseили используйтеasyncфункция. еслиuserIDКогда происходит изменение, новый запрос автоматически выполняется повторно. Результат кэшируется, поэтому запрос будет выполняться только один раз для каждого уникального ввода (поэтому убедитесь, что функция селектора сохранена чистой, иначе кэшированный результат будет несовместим с последним значением).

const userNameQuery = selector({
  key: 'userName',
  get: async ({get}) => {
    const response = await myDBQuery({
      userID: get(currentUserIDState),
    });
    return response.name;
  },
});

function CurrentUserInfo() {
  const userName = useRecoilValue(userNameQuery);
  return <div>{userName}</div>;
}

RecoilРекомендуемое использованиеSuspense,Suspenseзахватит все асинхронное состояние и будет сотрудничать сErrorBoundaryловить ошибки:

function MyApp() {
  return (
    <RecoilRoot>
      <ErrorBoundary>
        <React.Suspense fallback={<div>Loading...</div>}>
          <CurrentUserInfo />
        </React.Suspense>
      </ErrorBoundary>
    </RecoilRoot>
  );
}

Суммировать

RecoilВыступает за децентрализованное государственное управление, аналогичноеMobx, это также немного похоже наobservable + computedрежим, но его API и основные идеи не разработаныMobxЭто так же просто и легко понять, но это немного сложно, и для новичков будет определенная стоимость, чтобы начать работу.

полностью охватывает функциональныеHooksметод использования не обеспечиваетComponnentСпособ использования, на данный момент использую роднойHooks APIМы также можем реализовать управление состоянием, мы также можем использоватьuseMemoсоздать производное состояние,RecoilизuseRecoilStateа такжеselectorбольше похоже на правильноuseContext、useMemoупаковка.

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

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

Наконец

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

Эта статья впервые опубликована на паблике "Code Secret Garden". Просим обратить внимание всех. Оригинальный текст:Библиотека управления состоянием React нового поколения от Facebook Recoil