迁移到v2 API
参考: https://github.com/pmndrs/jotai/discussions/1514
Jotai v1 在2022年6月发布,收到了各种反馈。 React也提出了对promise的一流支持。 Jotai v2 将有一个新的API。
不幸的是,新功能伴随着一些破坏性的变化。
新功能是什么
Vanilla库
Jotai带有vanilla(非React)函数
和React函数分开。
它们从像jotai/vanilla
这样的备用入口点提供。
Store API
Jotai公开了store接口,以便您可以直接操作原子值。
import { createStore } from 'jotai' // 或者从 'jotai/vanilla'const store = createStore()store.set(fooAtom, 'foo')console.log(store.get(fooAtom)) // 打印 "foo"const unsub = store.sub(fooAtom, () => {console.log('fooAtom 在 store 中的值已经改变')})// 调用 unsub() 来取消订阅。
您也可以创建自己的React Context来传递一个store。
更灵活的原子 write 函数
write函数可以接受多个参数, 并返回一个值。
atom((get) => get(...),(get, set, arg1, arg2, ...) => {...return someValue})
什么是破坏性的
异步原子不再特殊
异步原子只是带有promise值的普通原子。
原子的getter函数不解析promises。
另一方面,useAtom
钩子继续解析promises。
像splitAtom
这样的一些工具期望同步原子,
并且不会与异步原子一起工作。
可写原子类型已更改(仅限TypeScript)
// 旧的WritableAtom<Value, Arg, Result extends void | Promise<void>>// 新的WritableAtom<Value, Args extends unknown[], Result>
一般来说,我们应该避免直接使用WritableAtom
类型。
一些函数被删除
- Provider的
initialValues
属性被删除,因为store
更灵活。 - Provider的scope属性被删除,因为你可以创建自己的context。
abortableAtom
工具被删除,因为该功能默认包含在内waitForAll
工具被删除,因为Promise.all
就可以工作
迁移指南
异步原子
异步原子的读函数的get
函数
不解析promises,所以你必须放await
或.then()
。
简单来说,改变就像下面这样。 (如果你是TypeScript用户,类型会告诉你在哪里改变。)
之前的API
const asyncAtom = atom(async () => 'hello')const derivedAtom = atom((get) => get(asyncAtom).toUppercase())
新的API
const asyncAtom = atom(async () => 'hello')const derivedAtom = atom(async (get) => (await get(asyncAtom)).toUppercase())// 或者const derivedAtom = atom((get) => get(asyncAtom).then((x) => x.toUppercase()))
Provider的initialValues属性
之前的API
const countAtom = atom(0)// 在组件中<Provider initialValues={[[countAtom, 1]]}>...
新 API
const countAtom = atom(0)const HydrateAtoms = ({ initialValues, children }) => {useHydrateAtoms(initialValues)return children}// 在组件中<Provider><HydrateAtoms initialValues={[[countAtom, 1]]}>...
Provider的 scope 属性
旧 API
const myScope = Symbol()// 父组件<Provider scope={myScope}>...</Provider>// 子组件useAtom(..., myScope)
新 API
const MyContext = createContext()const store = createStore()// 父组件<MyContext.Provider value={store}>...</MyContext.Provider>// 子组件const store = useContext(MyContext)useAtom(..., { store })
abortableAtom 工具
你不再需要之前的 abortableAtom
工具,
因为现在普通的 atom
已经支持了。
旧 API
const asyncAtom = abortableAtom(async (get, { signal }) => {...}
新 API
const asyncAtom = atom(async (get, { signal }) => {...}
waitForAll 工具
你不再需要之前的 waitForAll
工具,
因为我们可以使用原生的 Promise API。
旧 API
const allAtom = waitForAll([fooAtom, barAtom])
新 API
const allAtom = atom((get) => Promise.all([get(fooAtom), get(barAtom)]))
注意,在渲染函数中创建 atom 可能会导致无限循环
splitAtom 工具(或其他一些工具)与异步 atoms
splitAtom
工具只接受同步的 atoms。
你需要在传递之前解包异步 atoms。
这也适用于其他一些工具,如 jotai-tanstack-query
的 atomsWithQuery
。
旧 API
const splittedAtom = splitAtom(asyncArrayAtom)
新 API
const splittedAtom = splitAtom(unwrap(asyncArrayAtom, () => []))
在编写时,unwrap
是不稳定的,且未被文档记录。
你可以改用 loadable
,它在加载状态上提供了更多的控制。
如果你需要使用 <Suspense>
,atoms-in-atom 模式会有所帮助。
更多信息,请参考以下讨论:
- https://github.com/pmndrs/jotai/discussions/1615
- https://github.com/jotaijs/jotai-tanstack-query/issues/21
- https://github.com/pmndrs/jotai/discussions/1751
其他一些变化
工具
atomWithStorage
工具的delayInit
已被移除并默认启用。它将始终在首次渲染时渲染initialValue
,并在后续渲染时渲染存储的值(如果有)。新的行为与 v1 不同。更多信息请参见 https://github.com/pmndrs/jotai/discussions/1737 。useHydrateAtoms
只能接受可写的 atoms。
导入声明
v2 API 也为库作者和非 React 用户提供了从其他入口点导入的方式。
jotai/vanilla
jotai/vanilla/utils
jotai/react
jotai/react/utils
// 自 v1.11.0 起可用import { atom } from 'jotai/vanilla'import { useAtom } from 'jotai/react'// 自 v2.0.0 起可用import { atom } from 'jotai' // 与 'jotai/vanilla' 相同import { useAtom } from 'jotai' // 与 'jotai/react' 相同
注意:如果你不使用 ESM,你可能更倾向于使用 jotai/vanilla
等,而不是 jotai
,以便更好地进行树摇(tree shaking)。