- 动机
- 特性
- 核心概念
- 原子
- 选择器
- 使用
- 同步和计算属性
- 异步
- 完整示例
- 参考资源
Recoil 是什么
个人理解 Recoil 为 React 的外部状态管理提供了一种分布式+函数式的新的解决方案
selector 就是计算属性嘛,loadable 嘛还记得 dva-loading 嘛~
动机
出于兼容性和简便性的考虑,最好使用 React 的内置状态管理功能,而不要使用外部全局状态。但是 React 有一定的局限性:
- 只能通过将其提升到公共祖先来共享组件状态,但这可能包括一棵巨大的树,然后需要重新渲染。
- 上下文只能存储一个值,而不能存储不确定的一组值,每个值都有自己的使用者。
- 这两种方法都使得很难从树的叶子(使用状态的地方)代码分割树的顶部(状态必须存在的地方)。
我们希望在保持 API 以及语义和行为尽可能 Reactish 的同时进行改进。
- 我们获得了一个无样板的 API,其中共享状态具有与 React 本地状态相同的简单 get / set 接口(但如果需要,也可以用 reducer 等封装)。
- 我们有可能与并发模式和其他新的 React 功能兼容。
- 状态定义是增量式和分布式的,从而可以进行代码拆分。
- 可以用派生数据替换状态,而无需修改使用它的组件。
- 派生数据可以在同步和异步之间切换,而无需修改使用它的组件。
- 我们可以将导航视为 first-class 的概念,甚至可以对链接中的状态转换进行编码。
- 以向后兼容的方式持久保存整个应用程序状态很容易,因此持久保存的状态可以在应用程序更改后继续存在。
特性
-
最小和反应(Reactish)
Recoil 像 React 一样工作和思考。将一些添加到您的应用程序并获得快速灵活的共享状态。
-
数据流图
派生数据异步查询已通过纯函数和高效订阅方式实现。
-
跨应用观察
通过观察应用程序中所有状态的变化来实现持久性,路由,时间旅行调试或撤消,而不会影响代码拆分。
核心概念
原子
原子是状态单位。它们是可更新和可订阅的:当原子被更新时,每个被订阅的组件都将使用新值重新呈现。也可以在运行时创建。可以使用原子来代替 React 本地组件状态。如果多个组件使用相同的原子,则所有这些组件共享其状态。
原子需要一个唯一的密钥,该密钥可用于调试,持久性以及某些高级 API,这些 API 可让您查看所有原子的图。两个原子具有相同的密钥是一个错误,因此请确保它们在全局上是唯一的。像 React 组件状态一样,它们也具有默认值。
const textState = atom({
key: 'textState', // unique ID (with respect to other atoms/selectors)
default: '', // default value (aka initial value)
});
使用
const 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>
);
选择器
选择器是接受的原子或其它选择作为输入的纯函数。当这些上游原子或选择器更新时,将重新计算(re-evaluated)选择器。组件可以像原子一样订阅选择器,然后在选择器更改时将重新呈现它们。
选择器用于计算基于状态的派生数据。这使我们避免了冗余状态,通常无需使用 reduce 来保持状态同步和有效。取而代之的是,最小状态集存储在原子中,而其他所有内容都根据该最小状态有效地计算。由于选择器会跟踪需要哪些组件以及它们所依赖的状态,因此它们使这种功能方法更加有效。
从组件的角度来看,选择器和原子具有相同的界面,因此可以互相替换。
const charCountState = selector({
key: 'charCountState', // unique ID (with respect to other atoms/selectors)
get: ({ get }) => {
const text = get(textState);
return text.length;
},
});
使用
const CharacterCount = () => {
const count = useRecoilValue(charCountState);
return <>Character Count: {count}</>;
};
使用
同步和计算属性
参考概念的示例代码
异步
const encryptionState = selector({
key: 'asyncState',
get: async ({ get }) => {
const text = get(textState);
await delay(1000);
return text.replace(/./gi, '*');
},
});
const EncryptionCount = () => {
const contentLoadable = useRecoilValueLoadable(encryptionState);
try {
contentLoadable.getValue();
} catch (promise) {
console.log(promise === contentLoadable.toPromise()); // true
}
return (
<p>
Encryption content:
{contentLoadable.state === 'loading'
? 'loading'
: contentLoadable.getValue()}
</p>
);
};
完整示例
import React from 'react';
import ReactDOM from 'react-dom';
import delay from 'delay';
import {
RecoilRoot,
atom,
selector,
useRecoilState,
useRecoilValue,
useRecoilValueLoadable,
} from 'recoil';
const textState = atom({
key: 'textState', // unique ID (with respect to other atoms/selectors)
default: '', // default value (aka initial value)
});
const charCountState = selector({
key: 'charCountState', // unique ID (with respect to other atoms/selectors)
get: ({ get }) => {
const text = get(textState);
return text.length;
},
});
const encryptionState = selector({
key: 'asyncState',
get: async ({ get }) => {
const text = get(textState);
await delay(1000);
return text.replace(/./gi, '*');
},
});
const EncryptionCount = () => {
const contentLoadable = useRecoilValueLoadable(encryptionState);
try {
contentLoadable.getValue();
} catch (promise) {
console.log(promise === contentLoadable.toPromise()); // true
}
return (
<p>
Encryption content:
{contentLoadable.state === 'loading'
? 'loading'
: contentLoadable.getValue()}
</p>
);
};
const CharacterCount = () => {
const count = useRecoilValue(charCountState);
return <>Character Count: {count}</>;
};
const 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>
);
};
const CharacterCounter = () => {
return (
<div>
<TextInput />
<CharacterCount />
<EncryptionCount />
</div>
);
};
const App = () => {
return (
<RecoilRoot>
<CharacterCounter />
</RecoilRoot>
);
};
ReactDOM.render(<App />, document.getElementById('root'));
参考资源
最后分享一个”问题“网站,有兴趣的话,加我微信一起每天一个晓问题,共同进步,在“靠谱”这条路上越走越远:)