Recoil 的使用
|
通过简单的计数器应用来展示其使用。先来看没有 Recoil 时如何实现。 首先创建示例项目 $ yarn create react-app recoil-app --template typescript 计数器考察如下计数器组件: Counter.tsx import React, { useState } from "react";
跨组件共享数据状态当想把 Counter.tsx export interface ICounterProps {
Display.tsx export interface IDisplayProps {
App.tsx export function App() {
可以看到,数据被提升到了父组件中进行管理,而对数据的操作,也一并进行了提升,子组件中只负责触发改变数据的动作 这无疑增加了父组件的负担,一是这样的逻辑上升没有做好组件功能的内聚,二是父组件在最后会沉淀大量这种上升的逻辑,三是这种上升的操作不适用于组件深层嵌套的情况,因为要逐级传递属性。 当然,这里可使用 Context 来解决。 使用 Context 进行数据状态的共享添加 Context 文件保存需要共享的状态: appContext.ts import { createContext } from "react";
export const AppContext = createContext({
注意这里创建 Context 时,为了让子组件能够更新 Context 中的值,还额外创建了一个回调 更新 App.tsx export function App() {
更新 Counter.tsx export const Counter = () => {
更新 Display.tsx export const Display = () => {
可以看出,Context 解决了属性传递的问题,但逻辑上升的问题仍然存在。 同时 Context 还面临其他一些挑战,
export function App() {
Recoil 的使用安装添加 Recoil 依赖: $ yarn add recoil RecoilRoot类似 Context 需要将子组件包裹到 ReactDOM.render( Atom & SelectorRecoil 中最小的数据元作为 Atom 存在,从 Atom 可派生出其他数据,比如这里 创建 state 文件用于存放这些 Recoil 原子数据: appState.ts import { atom } from "recoil";
export const countState = atom({
通过 selector 可从基本的 atom 中派生出新的数据,假如还需要展示一个当前 import { atom, selector } from "recoil";
export const countState = atom({
selector 的存在意义在于,当它依赖的 atom 发生变更时,selector 代表的值会自动更新。这样程序中无须关于这些数据上的依赖逻辑,只负责更新最基本的 atom 数据即可。 而使用时,和 React 原生的 import { useRecoilState } from "recoil";
...
当只需要使用值而不需要对值进行修改时,可使用 Display.tsx import React from "react"; 由上面的使用可看到,atom 创建的数据和 selector 创建的数据,在使用上无任何区别。 当只需要对值进行设置,而又不进行展示时,则可使用 Conter.tsx import React from "react"; 异步数据的处理Recoil 最方便的地方在于,来自异步操作的数据可直接参数到数据流中。这在有数据来自于请求的情况下,会非常方便。 export const todoQuery = selector({
使用时,和正常的 state 一样: TodoInfo.tsx export function TodoInfo() {
但由于上面 App.tsx import React, { Suspense } from "react";
默认值前面看到无论 atom 还是 selector 都可在创建时指定默认值。而这个默认值甚至可以是来自异步数据。 appState.ts export const todosQuery = selector({
使用: TodoInfo.tsx export function TodoInfo() {
不使用 Suspense 的示例当然也可以不使用 React Suspense,此时需要使用 App.tsx import React from "react"; 给 selector 传参上面请求 Todo 数据时 id 是写死的,真实场景下,这个 id 会从界面进行获取然后传递到请求的地方。 此时可先创建一个 atom 用以保存该选中的 id。 export const idState = atom({
界面上根据交互更新 id,因为 export function App() {
另外处情况是直接将 id 传递到 selector,而不是依赖于另一个 atom。 export const todoQuery = selectorFamily<{ title: string }, { id: number }>({
App.tsx export function App() {
请求的刷新selector 是幂等的,固定输入会得到固定的输出。即,拿上述情况举例,对于给定的入参 id,其输出永远一样。根据这个我,Recoil 默认会对请求的返回进行缓存,在后续的请求中不会实际触发请求。 这能满足大部分场景,提升性能。但也有些情况,我们需要强制触发刷新,比如内容被编辑后,需要重新拉取。 有两种方式来达到强制刷新的目的,让请求依赖一个人为的 RequestId,或使用 Atom 来存放请求结果,而非 selector。 RequestId一是让请求的 selector 依赖于另一个 atom,可把这个 atom 作为每次请求唯一的 ID 亦即 RequestId。 appState.ts export const todoRequestIdState = atom({
让请求依赖于上面的 atom: export const todoQuery = selectorFamily<{ title: string }, { id: number }>({
然后在需要刷新请求的时候,更新 RequestId 即可。 App.tsx export function App() {
目前为止,虽然实现了请求的刷新,但观察发现,这里的刷新没有按资源 ID 来进行区分,点击刷新按钮后所有资源都重新发送了请求。
替换 atom 为 atomFamily 为其增加外部入参,这样可根据参数来决定刷新,而不是粗犷地全刷。 - export const todoRequestIdState = atom({
更新 RequestId 时传递需要更新的资源: export function App() {
上面刷新函数中写死了资源 ID,真实场景下,你可能需要写个自定义的 hook 来接收参数。 const useRefreshTodoInfo = (id: number) => {
使用 Atom 存放请求结果首先将获取 todo 的逻辑抽取单独的方法,方便在不同地方调用, export async function getTodo(id: number) {
通过 atomFamily 创建一个存放请求结果的状态: export const todoState = atomFamily<any, number>({
展示时通过这个 TodoInfo.tsx export function TodoInfo({ id }: ITodoInfoProps) {
在需要刷新的地方,更新 App.tsx function useRefreshTodo(id: number) {
注意,因为请求回来之后更新的是 Recoil 状态,所以需要在 useRecoilCallback 中进行。 异常处理前面的使用展示了 Recoil 与 React Suspense 结合用起来是多少顺滑,界面上的加载态就像呼吸一样自然,完全不需要编写额外逻辑就可获得。但还缺少错误处理。即,这些来自 Recoil 的异步数据请求出错时,界面上需要呈现。 而结合 React Error Boundaries 可轻松处理这一场景。 ErrorBoundary.tsx import React, { ReactNode } from "react";
// Error boundaries currently have to be classes.
/**
在所有需要错误处理的地方使用即可,理论上亦即所有出现 App.tsx <ErrorBoundary fallback="error :(">
ErrorBoudary 中展示错误详情上面的 ErrorBoundary 组件来自 React 官方文档,稍加改良可让其支持在错误处理时展示错误的详情: ErrorBoundary.tsx export class ErrorBoundary extends React.Component< 使用时接收错误参数并进行展示: App.tsx <ErrorBoundary fallback={(error: Error) => <div>{error.message}</div>}>
需要注意的问题selector 的嵌套与 Promise 的问题使用过程中遇到一个 selector 嵌套时 Promise 支持得不好的 bug,详见 Using an async selector in another selector, throws an Uncaught promise #694。 正如 bug 中所说,当 selector 返回异步数据,其他 selector 依赖于这个 selector 时,后续的 selector 会报 不过我发现,如果在后续 selector 中不使用 React Suspense 的 bug当使用文章前面提到的刷新功能时,数据刷新后,Suspense 中组件重新渲染,特定操作下会报
相关资源 |
|
The text was updated successfully, but these errors were encountered: |
Recoil 的使用的更多相关文章
- 14072202(带IK的Recoil)
[目标] 带IK的Recoil [思路] 1 继承于USkelControlLimb和UGameSkelCtrl_Recoil 2 效果对比 以这个骨骼为例 Recoil Limb 可见,Recoi ...
- JELLY技术周刊 Vol.24 -- 技术周刊 · 实现 Recoil 只需百行代码?
蒲公英 · JELLY技术周刊 Vol.24 理解一个轮子最好的方法就是仿造一个轮子,很多框架都因此应运而生,比如面向 JS 开发者的 AI 工具 Danfo.js:参考 qiankun 的微前端框架 ...
- Recoil & React official state management
Recoil & React official state management Redux Recoil.js https://recoiljs.org/ A state managemen ...
- Recoil 默认值及数据级联的使用
Recoil 中默认值及数据间的依赖 通过 Atom 可方便地设置数据的默认值, const fontSizeState = atom({ key: 'fontSizeState', default: ...
- Recoil 中多级数据联动及数据重置的合理做法
前情回顾 书接上回,前面引出了在数据存在级联的情况下,各下拉框之间的默认值及值变化的处理.简单回顾一下: 场景是: 地域下拉决定可选的可用区 默认选中第一个地域,通过设置 atom 的 default ...
- Recoil 中默认值的正确处理
继续使用 Recoil 默认值及数据级联的使用 的地域可用区级联的例子. 地域变更后可用区随之联动,两个下拉框皆默认选中第一个可选项. 从 URL 获取默认值 考虑这种情况,当 URL 中带了 que ...
- Recoil Input 光标位置被重置到末尾的问题
考察如下代码,页面中有个输入框,通过 Recoil Atom 来存储输入的值. App.tsx function NameInput() { const [name, setName] = useRe ...
- 14073102(CCDIKRecoil)
[目标] CCDIKRecoil [思路] 1 CCDIK和Recoil的结合 2 Recoil的回弹机制,逐渐回到原来位置 3 添加一个Recoil基类 [步骤] 1 将\Src\GameFrame ...
- apex-utility-ai-unity-survival-shooter
The AI has the following actions available: Action Function Shoot Fires the Kalashnikov Reload Reloa ...
随机推荐
- javascript algorithm visualization
javascript algorithm visualization javascript算法可视化 https://algorithm-visualizer.org https://github.c ...
- taro ENV & NODE_ENV & process.env
taro ENV & NODE_ENV & process.env https://github.com/NervJS/taro-ui/blob/dev/src/common/util ...
- css icons fontawesome-free
官网 examples v4.7.0 cdnjs icons basic-use 安装 λ npm install --save @fortawesome/fontawesome-free fa前缀在 ...
- 谁能成为数据储存领域领头羊?永久数据存储--NGK的终极使命!
区块链的目的是永远存储交易网络的历史.NGK技术团队能够永久存储其去中心化账本的副本.这是其日后能进行审计关键.一些著名的团队,如Solana和SKALE,现在正在为此与NGK进行最后的集成,我们预计 ...
- 阿里面试这样问:redis 为什么把简单的字符串设计成 SDS?
2021开工第一天,就有小伙伴私信我,还给我分享了一道他面阿里的redis题(这家伙绝比已经拿到年终奖了),我看了以后觉得挺有意思,题目很简单,是那种典型的似懂非懂,常常容易被大家忽略的问题.这里整理 ...
- Markdown简单语法的使用
Markdown简单语法的使用 目录 Markdown简单语法的使用 前言 标题的设置 字体的设置 1.字体加粗 2.斜体 3.字体加粗斜体 3.删除线 引用 列表的使用 插入图片 分割线 代码的书写 ...
- MySQL -- 内部临时表
本文转载自MySQL -- 内部临时表 UNION UNION语义:取两个子查询结果的并集,重复的行只保留一行 表初始化 CREATE TABLE t1(id INT PRIMARY KEY, a I ...
- uni-app小白入门自学笔记(二)
码文不易啊,转载请带上本文链接呀,感谢感谢 https://www.cnblogs.com/echoyya/p/14429616.html 目录 码文不易啊,转载请带上本文链接呀,感谢感谢 https ...
- 关于MVCC,我之前写错了,这次我改好了!
关于MVCC的原理,在<我想进大厂>之mysql夺命连环13问写过一次,但是当时写的其实并不准确,这个理解可以应付面试,帮助快速理解,但是他的真正实现原理我想再次拿出来说一说. 简单理解版 ...
- linux系统忘记root的登录密码
参考链接:https://www.jb51.net/article/146541.htm 亲测有效 使用场景 linux管理员忘记root密码,需要进行找回操作. 注意事项:本文基于centos7环 ...




