Facebook 新一代 React 状态管理库 Recoil

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

Recoil 如今还处于实验阶段,如今已经在 Facebook 一些内部产品中用于生产环境。毕竟是官方推出的状态管理框架,以前没时间仔细研究,借着国庆期间看了看,给你们分享一下。数据库

State 和 Context 的问题

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

最常规则作法是将一个 state 经过父组件分发给 ListCanvas 两个组件,显然这样的话每次 state 改变后 全部节点都会全量更新。设计模式

固然,咱们还可使用 Context API,咱们将节点的状态存在一个 Context 内,只要 Provider 中的 props 发生改变, Provider 的全部后代使用者都会从新渲染。缓存

为了不全量渲染的问题,咱们能够把每一个子节点存储在单独的 Context 中,这样每多一个节点就要增长一层 Providermarkdown

可是,若是子节点是动态增长的呢?咱们还须要去动态增长 Provider ,这会让整个树再次从新渲染,显然也是不符合预期的。并发

引入 Recoil

Recoil 自己就是为了解决 React 全局数据流管理的问题,采用分散管理原子状态的设计模式。框架

Recoil 提出了一个新的状态管理单位 Atom,它是可更新和可订阅的,当一个 Atom 被更新时,每一个被订阅的组件都会用新的值来从新渲染。若是从多个组件中使用同一个 Atom ,全部这些组件都会共享它们的状态。异步

你能够把 Atom 想象为为一组 state 的集合,改变一个 Atom 只会渲染特定的子组件,并不会让整个父组件从新渲染。async

用 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

相关文章
相关标签/搜索