Интенсивное чтение «отдачи»

JavaScript React.js

1. Введение

RecoilЭто решение для управления потоками данных, разработанное Facebook, которое имеет определенную ценность для размышлений.

Recoil — это решение для управления потоком данных, основанное на Immutable, и это самая важная причина, по которой на него стоит обратить внимание.Если вы хотите управлять потоком данных React с помощью Mutable, посмотрите непосредственно наmobx-reactБудет достаточно.

Однако предсказуемость, обеспечиваемая React Immutable, отлично подходит для отладки и обслуживания:

  1. Во время отладки точки останова значение переменной не имеет ничего общего с текущей позицией выполнения, и созданное значение не будет внезапно изменено Mutable, что очень предсказуемо.
  2. В среде React механизм обновления компонентов является единым, и только изменения ссылок вызывают повторный рендеринг без умственной нагрузки ForceUpdate в изменяемом режиме.

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

Но Recoil, как и Redux, не представляет официальное решение React для управления потоками данных, так что не смотрите на него с официальным ореолом.

2 Введение

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

государственная сфера

Как и Redux, управление глобальным потоком данных требует области действия.RecoilRoot:

import React from "react";
import { RecoilRoot } from "recoil";

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

RecoilRootПри вложении самый внутреннийRecoilRootОн переопределит значения конфигурации и состояния внешнего слоя.

определить данные

Централизованно определяется с помощью ReduxinitStateДругое, Отдача принимаетatomОпределите данные децентрализованным способом:

const textState = atom({
  key: "textState",
  default: "",
});

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

defaultОпределение значений по умолчанию, так как определения данных разбросаны, определения значений по умолчанию также разбросаны.

читать данные

Подобно Connect или useSelector Redux, Recoil использует хуки для чтения данных:

import { useRecoilValue } from "recoil";

function App() {
  const text = useRecoilValue(textState);
}

useRecoilValueа такжеuseSetRecoilStateданные можно получить, разницаuseRecoilStateВы также можете получить функции, которые записывают данные:

import { useRecoilState } from "recoil";

function App() {
  const [text, setText] = useRecoilValue(useRecoilState);
}

изменить данные

Централизованное определение чистых функций с помощью ReduxreducerВ отличие от модификации данных, Recoil использует хуки для записи данных.

Помимо упомянутого вышеuseRecoilStateКроме того, есть ещеuseSetRecoilStateМожно получить только функцию записи:

import { useSetRecoilState } from "recoil";

function App() {
  const setText = useSetRecoilValue(useRecoilState);
}

useSetRecoilStateа такжеuseRecoilState,useRecoilValueОтличие в том, что изменение потока данных не вызывает компонент Renderer, т.к.useSetRecoilStateТолько писать не читать.

Это также вызвало критику API Recoil.Это также умственное бремя кодирования в неизменяемом режиме.Хотя это хорошо понятно, это толькоuseSelectorИли способ разделения API, такой как Recoil, может решить эту проблему.

Также предоставляетсяuseResetRecoilStateСбросьте настройки по умолчанию и читайте.

Только чтение, не подписка

с ReactReduxuseStoreТочно так же Recoil обеспечиваетuseRecoilCallbackДля сценариев только для чтения и без подписки:

import { atom, useRecoilCallback } from "recoil";

const itemsInCart = atom({
  key: "itemsInCart",
  default: 0,
});

function CartInfoDebug() {
  const logCartItems = useRecoilCallback(async ({ getPromise }) => {
    const numItemsInCart = await getPromise(itemsInCart);

    console.log("Items in cart: ", numItemsInCart);
  });
}

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

производное значение

с МобксcomputedТочно так же отдача обеспечиваетselectorПоддерживаются производные значения, что является более отличительной чертой:

import { atom, selector, useRecoilState } from "recoil";

const tempFahrenheit = atom({
  key: "tempFahrenheit",
  default: 32,
});

const tempCelcius = selector({
  key: "tempCelcius",
  get: ({ get }) => ((get(tempFahrenheit) - 32) * 5) / 9,
  set: ({ set }, newValue) => set(tempFahrenheit, (newValue * 9) / 5 + 32),
});

function TempCelcius() {
  const [tempF, setTempF] = useRecoilState(tempFahrenheit);
  const [tempC, setTempC] = useRecoilState(tempCelcius);
}

selectorпри условииget,setОпределите, как присваивать и принимать значения соответственно, так что это то же самое, что иatomможно определить какuseRecoilStateВ ожидании трех наборов операций API, тут можно догадаться даже не заглядывая в исходный код,atomдолжно быть основано наselectorконкретный пакет .

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

на основеselectorАсинхронное чтение данных может быть достигнуто, еслиgetФункцию можно написать асинхронно:

const currentUserNameQuery = selector({
  key: "CurrentUserName",
  get: async ({ get }) => {
    const response = await myDBQuery({
      userID: get(currentUserIDState),
    });
    if (response.error) {
      throw response.error;
    }
    return response.name;
  },
});

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

function MyApp() {
  return (
    <RecoilRoot>
      <ErrorBoundary>
        <React.Suspense fallback={<div>Loading...</div>}>
          <CurrentUserInfo />
        </React.Suspense>
      </ErrorBoundary>
    </RecoilRoot>
  );
}
  1. Асинхронное состояние может бытьSuspenseзахватывать.
  2. Ошибки асинхронного процесса могут бытьErrorBoundaryзахватывать.

Если вы не хотите использоватьSuspenseБлокировка асинхронная, можно заменитьuseRecoilValueLoadableЭтот API управляет асинхронным состоянием в текущем компоненте:

function UserInfo({ userID }) {
  const userNameLoadable = useRecoilValueLoadable(userNameQuery(userID));
  switch (userNameLoadable.state) {
    case "hasValue":
      return <div>{userNameLoadable.contents}</div>;
    case "loading":
      return <div>Loading...</div>;
    case "hasError":
      throw userNameLoadable.contents;
  }
}

зависит от внешних переменных

а такжеreselectТочно так же Recoil также сталкивается с проблемой нечистого управления состоянием, то есть чтение данных зависит от внешних переменных, что столкнется с более сложными проблемами вычисления кэша, и даже появитсяre-reselectбиблиотека.

Поскольку Recoil сама по себе является атомарным управлением состоянием, эту проблему относительно легко решить:

const myMultipliedState = selectorFamily({
  key: "MyMultipliedNumber",
  get: (multiplier) => ({ get }) => {
    return get(myNumberState) * multiplier;
  },
});

function MyComponent() {
  const number = useRecoilValue(myMultipliedState(100));
}

При передаче внешних параметровmultiplierс зависимыми значениямиmyNumberStateПри неизменном значении он не будет пересчитываться.

Отдача вgetа такжеsetопределение функцииAtom, зависимости будут автоматически генерироваться внутри, и эта часть лучше.

Зависимые внешние переменные используют суффикс Family, например selector -> selectorFamily; atom -> atomFamily.

3 Интенсивное чтение

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

Неизменное умственное бремя

Существует множество API, которые также упоминаются во введении, и это может быть недостатком Immutable, а не только Recoil.

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

Отдача обеспечиваетuseRecoilStateВ качестве двойного API для чтения и записи он используется только в сценариях чтения и записи, иuseRecoilValueПросто для упрощения API замените наuseRecoilStateНет штрафа за производительность, в то время какuseSetRecoilValueК этому нужно относиться серьезно, и этот API должен строго использоваться в сценарии только записи, а не чтения.

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

Данные условного доступа

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

const articleOrReply = selectorFamily({
  key: "articleOrReply",
  get: ({ isArticle, id }) => ({ get }) => {
    if (isArticle) {
      return get(article(id));
    }

    return get(reply(id));
  },
});

Такой код на самом деле достаточно избыточен, его можно использовать в режиме Mutable.isArticle ? store.articles[id] : store.replies[id]Режим, который можно сделать, надо рисовать отдельноselectorОчень обременительно писать первые десять строк кода.

Суть отдачи

Эти две основные функции, от Hooks API до производных значений, являются инкапсуляцией Context и useMemo.

Сначала на основе крючковuseContextЛегкий, достаточно, чтобы использовать, можно считать, чтоatomа такжеuseRecoilState,useRecoilValue,useSetRecoilValueсоответствует упакованномуcreateContextа такжеuseContext.

посмотри сноваuseMemoБольшинство из нас может использоватьuseMemoСоздание значения ошибки, это соответствует отдачеselectorа такжеselectorFamily.

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

3 Резюме

Независимо от того, используете вы Recoil или нет, мы можем научиться основам управления состоянием React у Recoil:

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

Адрес обсуждения:Интенсивное чтение "отдача" · Выпуск №251 · dt-fe/weekly

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

Сфокусируйся наАккаунт WeChat для интенсивного чтения в интерфейсе

Заявление об авторских правах: Бесплатная перепечатка - некоммерческая - не производная - сохранить авторство (Лицензия Creative Commons 3.0)