乐趣区

关于前端:Facebook-新一代-React-状态管理库-Recoil

React Europe 2020 Conference 上,Facebook 软件工程师 Dave McCabe 介绍了一个新的状态治理库 Recoil

Recoil 当初还处于试验阶段,当初曾经在 Facebook 一些外部产品中用于生产环境。毕竟是官网推出的状态治理框架,之前没工夫认真钻研,借着国庆期间看了看,给大家分享一下。

State 和 Context 的问题

假如咱们有上面一个场景:有 ListCanvas 两个组件,List 中一个节点更新后,Canvas 中的节点也对应更新。

最常规定做法是将一个 state 通过父组件分发给 ListCanvas 两个组件,显然这样的话每次 state 扭转后 所有节点都会全量更新。

当然,咱们还能够应用 Context API,咱们将节点的状态存在一个 Context 内,只有 Provider 中的 props 产生扭转,Provider 的所有后辈使用者都会从新渲染。

为了防止全量渲染的问题,咱们能够把每个子节点存储在独自的 Context 中,这样每多一个节点就要减少一层 Provider

然而,如果子节点是动静减少的呢?咱们还须要去动静减少 Provider,这会让整个树再次从新渲染,显然也是不合乎预期的。

引入 Recoil

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:相似 useState 的一个 Hook,能够取到 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 函数,其作用与 reduxreselectMobX@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}</>;
}

selector 是一个纯函数:对于给定的一组输出,它们应始终产生雷同的后果(至多在应用程序的生命周期内)。这一点很重要,因为选择器可能会执行一次或屡次,可能会重新启动并可能会被缓存。

异步状态

Recoil 提供了通过数据流图将状态和派生状态映射到 React 组件的办法。真正弱小的性能是图中的函数也能够是异步的。这使得咱们能够在异步 React 组件渲染函数中轻松应用异步函数。应用 Recoil,你能够在选择器的数据流图中无缝地混合同步和异步性能。只需从选择器 get 回调中返回 Promise,而不是返回值自身。

例如上面的例子,如果用户名存储在咱们须要查问的某个数据库中,那么咱们要做的就是返回一个 Promise 或应用一个 async 函数。如果 userID 产生更改,就会主动从新执行新查问。后果会被缓存,所以查问将仅对每个惟一输出执行一次(所以肯定要保障 selector 纯函数的个性,否则缓存的后果将会和最新的值不统一)。

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 举荐应用 SuspenseSuspense 将会捕捉所有异步状态,另外配合 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 发明出派生状态,RecoiluseRecoilState 以及 selector 也比拟像是对 useContext、useMemo 的封装。

然而毕竟是 Facebook 官网推出的状态治理框架,其主打的是高性能以及能够利用 React 外部的调度机制,包含其承诺行将会反对的并发模式,这一点还是十分值得期待的。

另外,其自身的扩散治理原子状态的模式、读写拆散、按需渲染、派生缓存等思维还是十分值得一学的。

最初

文章中如有谬误,欢送在评论区斧正;如果文章对你有帮忙,欢送点赞、评论、分享、心愿能帮到更多人。

本文首发于公众号《code 机密花园》欢送大家关注,原文:Facebook 新一代 React 状态治理库 Recoil

退出移动版