Effect
jotai-effect 是 Jotai 的响应式副作用工具包。
npm install jotai-effectobserve
Section titled “observe”observe 将一个 effect 挂载到 Jotai store 上,用于监听状态变化。它适用于在 Store 级别运行全局副作用或逻辑。
如果你无法访问 store 对象且未使用默认 store,请改用 atomEffect 或 withAtomEffect。
type Cleanup = () => void
type Effect = ( get: Getter & { peek: Getter } set: Setter & { recurse: Setter }) => Cleanup | void
type Unobserve = () => void
function observe(effect: Effect, store?: Store): Unobserveeffect: 用于观察和响应原子状态变化的函数。
store: 挂载 effect 的 Jotai store。如果未提供,默认使用全局 store。
返回值: 一个稳定的函数,用于从 store 中移除 effect 并清理所有内部引用。
import { observe } from 'jotai-effect'
const unobserve = observe((get, set) => { set(logAtom, `someAtom changed: ${get(someAtom)}`)})
unobserve()这使你可以在 React 生命周期之外运行依赖 Jotai 状态的逻辑,非常适合应用级别的副作用。
在 React 中使用
Section titled “在 React 中使用”将 store 同时传递给 observe 和 Provider,以确保 effect 挂载到正确的 store 上。
const store = createStore()const unobserve = observe((get, set) => { set(logAtom, `someAtom changed: ${get(someAtom)}`)}, store)
<Provider store={store}>...</Provider>atomEffect
Section titled “atomEffect”atomEffect 创建一个原子,用于声明在挂载时响应状态变化的副作用。
function atomEffect(effect: Effect): Atom<void>effect: 用于观察和响应原子状态变化的函数。
import { atomEffect } from 'jotai-effect'
const logEffect = atomEffect((get, set) => { set(logAtom, get(someAtom)) // Runs on mount or when someAtom changes return () => { set(logAtom, 'unmounting') // Cleanup on unmount }})
// activates the atomEffect while Component is mountedfunction Component() { useAtom(logEffect)}withAtomEffect
Section titled “withAtomEffect”withAtomEffect 将一个 effect 绑定到目标原子的克隆上。当克隆的原子处于挂载状态时,effect 处于活跃状态。
function withAtomEffect<T>(targetAtom: Atom<T>, effect: Effect): Atom<T>targetAtom: effect 所绑定的原子。
effect: 用于观察和响应原子状态变化的函数。
返回值: 一个与目标原子等价但绑定了 effect 的原子。
import { withAtomEffect } from 'jotai-effect'
const valuesAtom = withAtomEffect(atom(null), (get, set) => { set(valuesAtom, get(countAtom)) return () => { // cleanup }})除挂载事件外,effect 会在其任何依赖的值发生变化时运行。
-
同步: 在 effect 内部通过
get访问的所有原子都会被添加为该原子的依赖。示例
atomEffect((get, set) => {// updates whenever `anAtom` changes valueget(anAtom)}) -
异步: 异步的
get调用不会添加依赖。示例
atomEffect((get, set) => {setTimeout(() => {// does not add `anAtom` as a dependencyget(anAtom)})}) -
清理: 清理函数中的
get调用不会添加依赖。示例
atomEffect((get, set) => {return () => {// does not add `anAtom` as a dependencyget(anAtom)}}) -
依赖映射重新计算: 依赖在每次运行时都会重新计算。
示例
atomEffect((get, set) => {if (get(isEnabledAtom)) {// `isEnabledAtom` and `anAtom` are dependenciesconst aValue = get(anAtom)} else {// `isEnabledAtom` and `anotherAtom` are dependenciesconst anotherValue = get(anotherAtom)}})
Effect 行为
Section titled “Effect 行为”-
同步执行:
effect在同步求值完成后,于当前任务中同步运行。示例
const logCounts = atomEffect((get, set) => {set(logAtom, `count is ${get(countAtom)}`)})const actionAtom = atom(null, (get, set) => {get(logAtom) // 'count is 0'set(countAtom, (value) => value + 1) // effect runs synchronouslyget(logAtom) // 'count is 1'})store.sub(logCounts, () => {})store.set(actionAtom) -
批量更新: 多个同步更新会被批处理为单个原子事务。
示例
const tensAtom = atom(0)const onesAtom = atom(0)const updateTensAndOnes = atom(null, (get, set) => {set(tensAtom, (value) => value + 1)set(onesAtom, (value) => value + 1)})const combos = atom([])const effectAtom = atomEffect((get, set) => {const value = get(tensAtom) * 10 + get(onesAtom)set(combos, (arr) => [...arr, value])})store.sub(effectAtom, () => {})store.set(updateTensAndOnes)store.get(combos) // [00, 11] -
抵抗无限循环:
atomEffect在更新它自身正在监听的值时,会避免重新运行。示例
atomEffect((get, set) => {get(countAtom)set(countAtom, (value) => value + 1) // Will not loop}) -
清理函数: 清理函数在卸载时或重新求值前调用。
示例
atomEffect((get, set) => {const intervalId = setInterval(() => set(clockAtom, Date.now()))return () => clearInterval(intervalId)}) -
幂等性:
atomEffect每次状态变化只运行一次,无论被引用多少次。示例
let i = 0const effectAtom = atomEffect(() => {get(countAtom)i++})store.sub(effectAtom, () => {})store.sub(effectAtom, () => {})store.set(countAtom, (value) => value + 1)console.log(i) // 1 -
条件性运行 Effect:
atomEffect仅在挂载时运行。示例
atom((get) => {if (get(isEnabledAtom)) {get(effectAtom)}}) -
支持 Peek: 使用
get.peek可以读取原子数据而不订阅。示例
const countAtom = atom(0)atomEffect((get, set) => {const count = get.peek(countAtom) // Will not add `countAtom` as a dependency}) -
支持递归: 通过
set.recurse支持递归,但不能在清理函数中使用。示例
atomEffect((get, set) => {const count = get(countAtom)if (count % 10 === 0) {return}set.recurse(countAtom, (value) => value + 1)})