recoil简介

什么问题

react组件间的状态共享只能通过将state提升至公共祖先实现,带来一个显而易见的问题就是开发效率低

快速入门

如需在组件中使用 Recoil,则可以将 RecoilRoot 放置在父组件的某个位置。将他设为根组件为最佳:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
import React from 'react';
import {
RecoilRoot,
atom,
selector,
useRecoilState,
useRecoilValue,
} from 'recoil';

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

一个 atom 代表一个状态。Atom 可在任意组件中进行读写。读取 atom 值的组件隐式订阅了该 atom,因此任何 atom 的更新都将致使使用对应 atom 的组件重新渲染:

1
2
3
4
const textState = atom({
key: 'textState', // unique ID (with respect to other atoms/selectors)
default: '', // default value (aka initial value)
});

在需要使用的组件中,你应该引入并使用 useRecoilState(),如下所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
function CharacterCounter() {
return (
<div>
<TextInput />
<CharacterCount />
</div>
);
}

function TextInput() {
const [text, setText] = useRecoilState(textState);

const onChange = (event) => {
setText(event.target.value);
};

return (
<div>
<input type="text" value={text} onChange={onChange} />
<br />
Echo: {text}
</div>
);
}

selector 代表一个派生状态,派生状态是状态的转换。你可以将派生状态视为将状态传递给以某种方式修改给定状态的纯函数的输出:

1
2
3
4
5
6
7
8
const charCountState = selector({
key: 'charCountState', // unique ID (with respect to other atoms/selectors)
get: ({get}) => {
const text = get(textState);

return text.length;
},
});

我们可以使用 useRecoilValue() 的 hook,来读取 charCountState 的值:

1
2
3
4
5
function CharacterCount() {
const count = useRecoilValue(charCountState);

return <>Character Count: {count}</>;
}

recoil的核心概念

Atom

Atom 是状态的单位。它们可更新也可订阅:

  • 当 atom 被更新,每个被订阅的组件都将使用新值进行重渲染。
  • 它们也可以在运行时创建。可以使用 atom 替代组件内部的 state。
  • 如果多个组件使用相同的 atom,则这些组件共享 atom 的状态。
  • Atom 需要一个唯一的 key 值
  • 要从组件中读取和写入 atom,我们需使用一个名为 useRecoilState 的 hook
1
2
3
4
5
6
7
8
9
10
11
12
13
14
const fontSizeState = atom({
key: 'fontSizeState',
default: 14,
});

function FontButton() {
const [fontSize, setFontSize] = useRecoilState(fontSizeState);
return (
<button onClick={() => setFontSize((size) => size + 1)} style={{fontSize}}>
Click to Enlarge
</button>
);
}

此时其他组件也可以使用相同的字体大小

1
2
3
4
5
6

function Text() {
const [fontSize, setFontSize] = useRecoilState(fontSizeState);
return <p style={{fontSize}}>This text will increase in size too.</p>;
}

Selector

  • selector 是一个纯函数,入参为 atom 或者其他 selector
  • 当上游 atom 或 selector 更新时,将重新执行 selector 函数
  • 组件可以像 atom 一样订阅 selector,当 selector 发生变化时,重新渲染相关组件。

类似于Vue的computed,Selector 被用于计算基于 state 的派生数据。这使得我们避免了冗余 state,我们只需要将最小粒度存储在Atom中,其他所有内容根据最小粒度进行有效计算

1
2
3
4
5
6
7
8
9
10
11
// 在这个 fontSizeLabelState 示例中,selector 包含一个依赖项:fontSizeState atom. 从概念上讲,fontSizeLabelState selector的行为与纯函数类似,该函数将 fontSizeState 作为输入,并将格式化后的字体大小的 label 值作为返回值。
const fontSizeLabelState = selector({
key: 'fontSizeLabelState',
get: ({get}) => {
const fontSize = get(fontSizeState);
const unit = 'px';

return `${fontSize}${unit}`;
},
});

get 属性是用于计算的函数。它可以使用名为 get 的入参来访问 atom 以及其他 selector 的值。每当它访问另一个 atom 或 selector 时,就会创建相应的依赖关系,以便在更新另一个 atom 或 selector 时,该 atom 或 selector 也会被重新计算。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// 可以使用 useRecoilValue() 读取 selector。它使用 atom 或 selector 作为参数,并返回相应的值。我们在这里不使用 useRecoilState(),因为fontSizeLabelState selector 不可写
function FontButton() {
const [fontSize, setFontSize] = useRecoilState(fontSizeState);
const fontSizeLabel = useRecoilValue(fontSizeLabelState);

return (
<>
<div>Current font size: {fontSizeLabel}</div>

<button onClick={() => setFontSize(fontSize + 1)} style={{fontSize}}>
Click to Enlarge
</button>
</>
);
}

关于上面selector不可写的问题,参考selector Api

指南

相关链接

官网
中文文档

Author: sumshare
Link: http://blog.sumshare.cn/2021/09/05/recoil/
Copyright Notice: All articles in this blog are licensed under CC BY-NC-SA 4.0 unless stating additionally.