Jotai 实战(上)

早前,我们介绍了《从 RecoilJotai》 的迁移。其实随着迁移的过程中,也有着一些实践性的好方法,这里做一个总结提炼。

文章中的代码地址:github.com/bigbigDream... 快捷传送门

大纲

  1. [atom 细粒度作用域控制](#atom 细粒度作用域控制 "###1")
  2. [atom 懒请求](#atom 懒请求 "###2")
  3. [atom 本地缓存](#atom 本地缓存 "###3")
  4. [atom 动态 key](#atom 动态 key "###4")

1. atom 细粒度作用域控制

目的:实现不同层级的状态管理,例如:组件层页面层应用层 的细粒度控制。

实现方案

在不同的作用域层级添加 Provider ,作为隔离效果,还有一个好处是:当重新加载时,挂在 Provider 的所有 Atom 会被重置状态。后者才是最为好用的一个能力。

tsx 复制代码
import { Provider } from 'jotai'

const Component: FC = ({ children }) => <Provider>{ children }</Provider>

2. atom 懒请求

目的:在一个方法中,同时做到 atomgetset ,避免写过多的样板代码。

这里写了一个 demo ,大概就是每次滚动到底部时,去主动触发一次请求。

实现方案

这里仅列举最核心的伪代码,减轻阅读负担。

ts 复制代码
// some code
const loadMoreData = useAtomCallback(useCallback(async (get, set) => {  
    const _data = get(globalData);  
    await sleep(1500);  
    set(globalData, {data: [..._data.data, ...genUsers()]})  
    message.success("data load success!")  
}, []));  
  
useEffect(() => {  
    loadMoreData()  
}, [])

可以看到,我们借助了 useAtomCallback 来实现这个能力,而 useCallback 的作用是为了维持 useAtomCallback 内部的函数引用唯一,避免加载 死循环

重点:使用 useAtomCallback 时,必须在整个组件上下文中去 subscribe atom ,简单点就是 useAtom 或者 useAtomValue,否则无法 get 最新的内容。

3. atom 本地缓存

目的:实现 本地缓存atom 结合。

这里写了一个 demo ,目的是为了将 二维码 的地址,持久化到 IndexDB ,同时满足 f(UI) 的需要。

实现方案

store.ts

我们借助 localforage 来实现降级,但是内部我们做了最终降级,假设 IndexDBwebSQLLocalStorage 都不支持,我们最终降级到 memory map 中,保证程序运转是正常的。

ts 复制代码
import { atomWithStorage } from 'jotai/utils';  
import localforage from "localforage";  
import to from 'await-to-js';  
  
const localforageInstance = localforage.createInstance({  
    storeName: 'LocalPageStore',  
    version: 1  
})  
const memoryCache = new Map();  
const localForageStore: Parameters<typeof atomWithStorage>[2] = {  
    async setItem(key, value) {  
        const [err] =  await to(localforageInstance.setItem(key, value));  
        if(err) {  
            memoryCache.set(key, value);  
        }  
    },  
    async getItem(key) {  
        const [err, result] =  await to(localforageInstance.getItem(key));  
        if(err) {  
            return memoryCache.get(key) || null;  
        }  
        return  result  
    },  
  
    async removeItem(key) {  
        const [err] =  await to(localforageInstance.removeItem(key));  
        if(err) {  
            memoryCache.delete(key);  
        }  
    }  
}  
  
export const pageStorageStore = atomWithStorage('LocalCache', null, localForageStore)

4. atom 动态 key

目的:实现一个 atom 引用,可以作用多个 atom 的读写。

这里写了一个 demo ,拆分两个 atom ,一个控制 checked ,一个控制 文案的显示

实现方案

store.ts

ts 复制代码
import { atomFamily } from 'jotai/utils';  
import {WritableAtom, atom} from "jotai";  
  
const keyMapAtom: {  
    [StoreKey.CHECK_STORE]: WritableAtom<boolean, boolean[], void>;  
    [StoreKey.CONTENT_STORE]: WritableAtom<string, string[], void>  
} = {  
    CHECK_STORE: atom(false),  
    CONTENT_STORE: atom('')  
}  
export const pageStore = atomFamily<StoreKey, WritableAtom<boolean, boolean[], void> | WritableAtom<string, string[], void>>(key => keyMapAtom[key]);  
  
export enum StoreKey {  
    CHECK_STORE = 'CHECK_STORE',  
    CONTENT_STORE = 'CONTENT_STORE'  
}

index.tsx

ts 复制代码
// checked atom
const [checked, setChecked] = useAtom<boolean, [boolean], void>(pageStore(StoreKey.CHECK_STORE) as WritableAtom<boolean, boolean[], void>) 
// content atom
const [content, setContent] = useAtom<string, [string], void>(pageStore(StoreKey.CONTENT_STORE) as WritableAtom<string, string[], void>)

const handleCheck = () => {  
    setChecked(!checked)  
    if(checked) {  
        setContent("Not Checked")  
    } else {  
        setContent("Checked")  
    }  
}  

const checkContainerCls = classes('check-container', {  
    'check-container-switch': checked  
})  
// memory GC
useEffect(() => () => {  
    pageStore.setShouldRemove(() => true);  
})

我们可以借助 atomFamily 来实现 paramsatom ,这其实对于 fetch atom 很友好,可以实现不同参数的 fetch,尤其是 url params 请求。

但是 atomFamily 我们在 下篇介绍过,会存在内存泄漏的风险,因为内部使用了 Map 作为不自动回收的存储数据结构。

所以我们借助了 setShouldRemove 在页面 卸载 时,进行全量清理。


好了,Jotai BPs 上篇 到这里就结束了。

我是 不换,如果写的不正确,欢迎评论区批评指正,如果对你有帮助,请点个小赞,赠人玫瑰,手有余香。我们下期再见👋。

相关推荐
中微子5 分钟前
JavaScript 防抖与节流:从原理到实践的完整指南
前端·javascript
天天向上102420 分钟前
Vue 配置打包后可编辑的变量
前端·javascript·vue.js
芬兰y36 分钟前
VUE 带有搜索功能的穿梭框(简单demo)
前端·javascript·vue.js
好果不榨汁43 分钟前
qiankun 路由选择不同模式如何书写不同的配置
前端·vue.js
小蜜蜂dry43 分钟前
Fetch 笔记
前端·javascript
拾光拾趣录44 分钟前
列表分页中的快速翻页竞态问题
前端·javascript
小old弟1 小时前
vue3,你看setup设计详解,也是个人才
前端
Lefan1 小时前
一文了解什么是Dart
前端·flutter·dart
Patrick_Wilson1 小时前
青苔漫染待客迟
前端·设计模式·架构
vvilkim1 小时前
Nuxt.js 全面测试指南:从单元测试到E2E测试
开发语言·javascript·ecmascript