前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >从 Recoil 到 Jotai (上)

从 Recoil 到 Jotai (上)

作者头像
不换
发布2024-04-30 13:58:01
760
发布2024-04-30 13:58:01
举报

背景

偶然的一次项目开发中发现 Recoil 有内存泄漏的情况,一次几 M ,虽然影响不大,但是还是具有一定的隐患,特别是随着项目的体量增加,后期带来的替换成本也会随之增高;

当然这是原因之一,下面会跟大家聊聊 Recoil 的现状。

现状

  • Recoil 仍旧处于实验性质
  • Meta 官方宣布将 Recoil 开放给外部维护者;(来自 Discord,尚未证实)
  • 核心开发者 drarmstr 已于 20231 月被解雇;(已证实LinkedIn)
  • FaceBook 官网仍在使用 Recoil,并且将其作为生产应用来使用;

其实抛开上述现状来讲,内部项目在生产使用 Recoil 并无什么大的问题,偶现场景的内存泄漏,基本属于极端场景,但是基于长远发展的方向而言,库的选型上可能略有不妥;

但是不的不吐槽 Recoil 的包体积确实很大,这也是我下决心替换库的最大动机。

Recoil VS Jotai

参考:

  • Jotai 讨论区
  • VS Blog

Jotai 相比较 Recoil 主要有几个区别:

  • atom 作为 key 比起 唯一string key,样板代码变少了、内存泄漏风险减少了;
  • 包体积更小;
  • API 更简单容易理解,可拔插、集成能力更优异;
  • 维护者更积极,BUG & Feature 响应更快;
  • 性质不同,一个是稳定性质一个是实验性质;

Jotai

新的 原子型 状态管理库的后备方案;

核心维护者是 Dai-shi,目前有 14.9k+ 星星,受众公司也蛮多;

聊到这里,咱们可以步入正题了,我准备从 原子型 状态的思想、recoiljotaiAPI 迁移指南入题讲解。

原子哲学

jotai 为例,底层还是依赖了 React Provider (这里解释不包含 provider less mode)作为原子范围的隔离;

下面是 GC 原理,区别于 RecoilString key 作为键更稳定)

图片来自于:https://excalidraw.com/

动机:

  • 解决 provider 嵌套地狱 & 渲染地狱
  • 跨组件状态共享更加便捷;
  • 精准渲染订阅组件,避免 reRender

jotai 基础课程

Provider Less Mode

Provider 模式,有点类似于 React 18 中的 useSyncExternalStore

本质上还是借助 发布订阅模式代理模式,对订阅组件执行 forceUpdate 操作。

jotai 自己手撸了一个 源码,借助 WeakMap 实现的。

代码语言:javascript
复制

// source code from https://github.com/pmndrs/jotai/blob/main/src/vanilla/store.ts

export const createStore = () => {
  const atomStateMap = new WeakMap<AnyAtom, AtomState>()
  const mountedMap = new WeakMap<AnyAtom, Mounted>()
  const pendingMap = new Map<
    AnyAtom,
    AtomState /* prevAtomState */ | ?developer/article/2414008/undefined
  >()
 // ....
}

那么最终的原子哲学是如何工作的呢?

图片来自于:https://blog.bitsrc.io/redux-free-state-management-with-jotai-2c8f34a6a4a

浅看一下 useAtomValue 的源码:https://github.com/pmndrs/jotai/blob/2526039ea4da082749adc8a449c33422c53d9819/src/react/useAtomValue.ts

代码语言:javascript
复制
useEffect(() => {
    const unsub = store.sub(atom, () => {
      if (typeof delay === 'number') {
        // delay rerendering to wait a promise possibly to resolve
        setTimeout(rerender, delay)
        return
      }
      rerender()
    })
    rerender()
    return unsub
  }, [store, atom, delay])

本质就是发布订阅、观察者模式那一套。

Recoil -> Jotai

atom
  1. recoil
代码语言:javascript
复制
import { atom } from 'recoil';

const todosAtom = atom({
 key: 'TODO_STORE',
 default: {
  data: []
 }
})
  1. jotai
代码语言:javascript
复制
import { atom } from 'jotai';

const todosAtom = atom({
 data: []
})
useRecoilState -> useAtom
  1. recoil
代码语言:javascript
复制
import { useRecoilState } from 'recoil';

const [data, setData] = useRecoilState(todosAtom);
  1. jotai
代码语言:javascript
复制
import { useAtom } from 'jotai';

const [data, setData] = useRecoilState(todosAtom);
useSetRecoilState -> useSetAtom
  1. recoil
代码语言:javascript
复制
import { useSetRecoilState } from 'recoil';

const setData = useRecoilState(todosAtom);
  1. jotai
代码语言:javascript
复制
import { useSetAtom } from 'jotai';

const setData = useSetAtom(todosAtom);
useRecoilValue -> useAtomValue
  1. recoil
代码语言:javascript
复制
import { useRecoilValue } from 'recoil';

const data = useRecoilValue(todosAtom);
  1. jotai
代码语言:javascript
复制
import { useAtomValue } from 'jotai';

const data = useAtomValue(todosAtom);
useResetRecoilState -> useResetAtom
  1. recoil
代码语言:javascript
复制
import { useResetRecoilState } from 'recoil';

const reset = useResetRecoilState(todosAtom);
  1. jotai

如果你在 jotai 中定义的原子需要具备 resettable 能力,这里有两个注意事项:

  • 原子定义方式需要变化
  • API 导入变化

原子定义

代码语言:javascript
复制
import { atomWithReset, useResetAtom, RESET } from 'jotai/utils'

const todosAtom = atomWithReset({ data: [] })

API导入

代码语言:javascript
复制
import { useResetAtom } from 'jotai/utils'

const reset = useResetAtom(todosAtom)
useRecoilCallback -> useAtomCallback
  1. recoil
代码语言:javascript
复制
import { useRecoilCallback } from 'recoil';


const lazyLoad = useRecoilCallback(({ set, reset, snapshot }) => async () => {
 const data = snapshot.getPromise(todosAtom);
 // 打印 atom data
}, [])
  1. jotai
代码语言:javascript
复制
import { useCallback } from 'react';
import { useAtomCallback } from 'jotai/utils'

const lazyload = useAtomCallback(useCallback(async (get, set) => {
 const data = await get(todosAtom);
}, []));

到这里,从 recoiljotai 迁移的上篇就结束了,从 API 的迁移上看,核心的 API 迁移成本还是很小的,代码略微改造即可。

下篇预告:下篇则会着重介绍 异步原子 的迁移,各种实战 hack 写法,我们下期再见!

本文参与?腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2023-09-12,如有侵权请联系?cloudcommunity@tencent.com 删除

本文分享自 不换的随想乐园 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与?腾讯云自媒体分享计划? ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 背景
  • 现状
  • Recoil VS Jotai
  • Jotai
  • 原子哲学
  • Provider Less Mode
  • Recoil -> Jotai
    • atom
      • useRecoilState -> useAtom
        • useSetRecoilState -> useSetAtom
          • useRecoilValue -> useAtomValue
            • useResetRecoilState -> useResetAtom
              • useRecoilCallback -> useAtomCallback
              领券
              问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
              http://www.vxiaotou.com