Scope
有一些库可以扩展 Jotai 在 React 中的用法。
jotai-scope
Section titled “jotai-scope”虽然 Jotai 的 Provider 允许在子树下限定 Jotai Store 的作用域,但我们无法在子树中使用上层树的 Store。
一种变通方法是在 useAtom 和其他 hook 中使用 store 选项。
与指定 store 选项不同,<ScopeProvider> 允许你在 React 树的不同部分复用相同的原子,而不共享状态,同时仍然能够从父级 Store 读取其他原子。
npm install jotai-scopeimport { atom, useAtom } from 'jotai'import { ScopeProvider } from 'jotai-scope'
const countAtom = atom(0)
function Counter() { const [count, setCount] = useAtom(countAtom) const [anotherCount, setAnotherCount] = useAtom(anotherCountAtom) return ( <> <div> <span>count: {count}</span> <button type="button" onClick={() => setCount((v) => v + 1)}> increment </button> </div> <div> <span>another count: {anotherCount}</span> <button type="button" onClick={() => setAnotherCount((v) => v + 1)}> increment </button> </div> </> )}
function App() { return ( <div> <Counter /> <ScopeProvider atoms={[countAtom]}> <Counter /> </ScopeProvider> </div> )}createIsolation
Section titled “createIsolation”Jotai 的 Provider 和 jotai-scope 的 scoped provider 仍然使用全局 context。
如果你正在开发一个依赖 Jotai 的库,而库的用户可能在他们的应用中单独使用 Jotai,那么它们会共享同一个 context。这可能会导致问题,因为它们会指向意料之外的 Jotai Store。
为了避免 context 冲突,jotai-scope 导出了一个名为 createIsolation 的工具函数。
import { createIsolation } from 'jotai-scope'
const { Provider, useStore, useAtom, useAtomValue, useSetAtom } = createIsolation()
function Library() { return ( <Provider> <LibraryComponent /> </Provider> )}bunshi(原名 jotai-molecules)
Section titled “bunshi(原名 jotai-molecules)”Jotai 原子提供了优化重渲染的基本方案。全局定义的原子可以依赖其他原子,但无法依赖组件树中的 props 和 state。虽然可以在组件树内定义原子,但你需要通过某种方式传递这些原子(例如,atoms-in-atom)。
bunshi 是一个第三方库,专门用于处理此类场景。
更多详情请参阅 Motivation。
npm install bunshiimport { atom, useAtom } from 'jotai'import { molecule, useMolecule, createScope, ScopeProvider } from 'bunshi/react'
const InitialCountScope = createScope({ initialCount: 0 })const countMolecule = molecule((getMol, getScope) => { const { initialCount } = getScope(InitialCountScope) return atom(initialCount)})
function Counter() { const countAtom = useMolecule(countMolecule) const [count, setCount] = useAtom(countAtom) return ( <div> {count} <button onClick={() => setCount((v) => v + 1)}>+1</button> </div> )}
function App() { return ( <div> <h1>With initial value 1</h1> <ScopeProvider scope={InitialCountScope} value={{ initialCount: 1 }}> <Counter /> <Counter /> </ScopeProvider> <h1>With initial value 2</h1> <ScopeProvider scope={InitialCountScope} value={{ initialCount: 2 }}> <Counter /> <Counter /> </ScopeProvider> <h1>Default</h1> <Counter /> <Counter /> </div> )}