大对象
以下的例子和描述基于这个codesandbox,所以如果你在阅读这些例子的同时查看它,会有更好的理解。
有时我们需要在原子中存储嵌套数据,并且我们可能需要在不同的级别更改这些数据,或者我们需要使用部分数据而不监听所有的更改。
考虑这个例子:
const initialData = {people: [{name: 'Luke Skywalker',information: { height: 172 },siblings: ['John Skywalker', 'Doe Skywalker'],},{name: 'C-3PO',information: { height: 167 },siblings: ['John Doe', 'Doe John'],},],films: [{title: 'A New Hope',planets: ['Tatooine', 'Alderaan'],},{title: 'The Empire Strikes Back',planets: ['Hoth'],},],info: {tags: ['People', 'Films', 'Planets', 'Titles'],},}
focusAtom
focusAtom
创建一个新的原子,基于你传递给它的焦点。jotai-optics
我们使用这个工具来聚焦一个原子并从数据的特定部分创建一个原子。例如,我们可能需要消耗上述数据的 people 属性,这是我们如何做的:
import { atom } from 'jotai'import { focusAtom } from 'jotai-optics'const dataAtom = atom(initialData)const peopleAtom = focusAtom(dataAtom, (optic) => optic.prop('people'))
focusAtom
返回 WritableAtom
,这意味着可以更改 peopleAtom
数据。
如果我们更改上述数据示例的 films
属性,peopleAtom
不会导致重新渲染,这是使用 focusAtom
的好处之一。
splitAtom
当你想要为列表中的每个元素获取一个原子时,
splitAtom
工具很有用。jotai/utils
我们使用这个工具来处理返回数组作为其值的原子。例如,我们上面创建的 peopleAtom
返回 people 属性数组,所以我们可以为该数组的每个项返回一个原子。如果数组原子是可写的,splitAtom
返回的原子将是可写的,如果数组原子是只读的,返回的原子也将是只读的。
import { splitAtom } from 'jotai/utils'const peopleAtomsAtom = splitAtom(peopleAtom)
这就是我们在组件中如何使用它。
const People = () => {const [peopleAtoms] = useAtom(peopleAtomsAtom)return (<div>{peopleAtoms.map((personAtom) => (<Person personAtom={personAtom} key={`${personAtom}`} />))}</div>)}
selectAtom
这个函数创建一个派生原子,其值是原始原子值的函数。jotai/utils
这个工具类似于 focusAtom
,但我们在有一个只读原子时使用它来选择它的一部分,它总是返回一个只读 原子。
假设我们想要消耗 info 数据,并且其数据总是不可更改的。我们可以从它创建一个只读原子并选择该创建的原子。
// 首先我们基于 initialData.info 创建一个只读原子const readOnlyInfoAtom = atom((get) => get(dataAtom).info)
然后我们在我们的组件中使用它:
import { atom, useAtom } from 'jotai'import { selectAtom, splitAtom } from 'jotai/utils'const tagsSelector = (s) => s.tagsconst Tags = () => {const tagsAtom = selectAtom(readOnlyInfoAtom, tagsSelector)const tagsAtomsAtom = splitAtom(tagsAtom)const [tagAtoms] = useAtom(tagsAtomsAtom)return (<div>{tagAtoms.map((tagAtom) => (<Tag key={`${tagAtom}`} tagAtom={tagAtom} />))}</div>)}