跳至主要内容

核心概念

概述

Recoil 允许你创建一个数据流图,该数据流图从原子(共享状态)流经选择器(纯函数),然后流向你的 React 组件。原子是状态的单位,组件可以订阅它们。选择器同步或异步地转换此状态。

原子

原子是状态的单位。它们是可更新和可订阅的:当原子更新时,每个订阅的组件都会使用新值重新渲染。它们也可以在运行时创建。原子可以用来代替 React 本地组件状态。如果多个组件使用同一个原子,则所有这些组件都会共享它们的 state。

原子是使用 atom 函数创建的

const fontSizeState = atom({
key: 'fontSizeState',
default: 14,
});

原子需要一个唯一的键,该键用于调试、持久化和某些高级 API,这些 API 允许你查看所有原子的映射。两个原子拥有相同的键是错误的,因此请确保它们在全局范围内是唯一的。与 React 组件状态一样,它们也有一个默认值。

要从组件读取和写入原子,我们使用一个名为 useRecoilState 的钩子。它与 React 的 useState 类似,但现在状态可以在组件之间共享

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

点击按钮会将按钮的字体大小增加 1。但现在其他一些组件也可以使用相同的字体大小

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

选择器

选择器是一个纯函数,它接受原子或其他选择器作为输入。当这些上游原子或选择器更新时,选择器函数将被重新计算。组件可以像订阅原子一样订阅选择器,然后在选择器发生变化时重新渲染。

选择器用于计算基于 state 的派生数据。这使我们能够避免冗余状态,因为一组最小的 state 存储在原子中,而所有其他 state 则作为该最小 state 的函数高效地计算出来。由于选择器会跟踪哪些组件需要它们以及它们依赖于哪些 state,因此它们使得这种函数式方法非常高效。

从组件的角度来看,选择器和原子具有相同的接口,因此可以相互替换。

选择器是使用 selector 函数定义的

const fontSizeLabelState = selector({
key: 'fontSizeLabelState',
get: ({get}) => {
const fontSize = get(fontSizeState);
const unit = 'px';

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

get 属性是要计算的函数。它可以使用传递给它的 get 参数访问原子和其他选择器的值。每当它访问另一个原子或选择器时,就会创建一个依赖关系,这样更新另一个原子或选择器会导致这个选择器重新计算。

在这个 fontSizeLabelState 示例中,选择器有一个依赖项:fontSizeState 原子。从概念上讲,fontSizeLabelState 选择器表现得像一个纯函数,它以 fontSizeState 作为输入,并返回格式化的字体大小标签作为输出。

选择器可以使用 useRecoilValue() 读取,它以原子或选择器作为参数,并返回相应的值。我们不使用 useRecoilState(),因为 fontSizeLabelState 选择器不可写(有关可写选择器的更多信息,请参见 选择器 API 参考

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>
</>
);
}

点击按钮现在会做两件事:它会增加按钮的字体大小,同时也会更新字体大小标签以反映当前字体大小。