跳转到内容

Atoms in atom

atom() 创建的是一个原子配置对象,它本身不持有值。 原子配置没有字符串键,我们通过引用相等性来标识它们。 换句话说,我们可以把原子配置当作键来使用。

首先,我们可以在 useState 中存储原子配置。

const Component = ({ atom1, atom2 }) => {
const [selectedAtom, setSelectedAtom] = useState(atom1)
const [value] = useAtom(selectedAtom)
return (
<div>
Selected value: {value}
<button onClick={() => setSelectedAtom(atom1)}>Select an atom</button>
<button onClick={() => setSelectedAtom(atom2)}>
Select another atom
</button>
</div>
)
}

注意,我们可以将原子配置作为 props 传递。

虽然看起来可能没有什么实际意义,但我们确实可以按需创建原子配置。

const Component = () => {
const [currentAtom, setCurrentAtom] = useState(() => atom(0))
const [count, setCount] = useAtom(currentAtom)
return (
<div>
Count: {count} <button onClick={() => setCount((c) => c + 1)}>+1</button>
<button onClick={() => setCurrentAtom(atom(0))}>Create new</button>
</div>
)
}

同样,我们可以将一个原子配置作为另一个原子的值来存储。

const firstNameAtom = atom('Tanjiro')
const lastNameAtom = atom('Kamado')
const showingNameAtom = atom(firstNameAtom)
const Component = () => {
const [nameAtom, setNameAtom] = useAtom(showingNameAtom)
const [name] = useAtom(nameAtom)
return (
<div>
Name: {name}
<button onClick={() => setNameAtom(firstNameAtom)}>
Show First Name
</button>
<button onClick={() => setNameAtom(lastNameAtom)}>Show Last Name</button>
</div>
)
}

也可以创建派生原子。

const derivedNameAtom = atom((get) => {
const nameAtom = get(showingNameAtom)
return get(nameAtom)
})
// 或者更简短的写法
const derivedNameAtom = atom((get) => get(get(showingNameAtom)))

为了避免混淆原子中存储的内容,明确地命名原子很重要。 同时,TypeScript 的类型信息也会有所帮助。

最后,“原子中的原子”模式就是将原子配置数组存储到一个原子中。

const countsAtom = atom([atom(1), atom(2), atom(3)])
const Counter = ({ countAtom }) => {
const [count, setCount] = useAtom(countAtom)
return (
<div>
{count} <button onClick={() => setCount((c) => c + 1)}>+1</button>
</div>
)
}
const Parent = () => {
const [counts, setCounts] = useAtom(countsAtom)
const addNewCount = () => {
const newAtom = atom(0)
setCounts((prev) => [...prev, newAtom])
}
return (
<div>
{counts.map((countAtom) => (
<Counter countAtom={countAtom} key={countAtom} />
))}
<button onClick={addNewCount}>Add</button>
</div>
)
}

这种方式的好处是:当你递增某个计数时,只有对应的 Counter 组件会重新渲染,其他组件不会重新渲染。

需要注意的是,anAtom.toString() 会返回一个唯一 ID,可以用作 map 中的 key

<Counter countAtom={countAtom} key={`${countAtom}`} />

同样,我们可以存储对象映射来代替数组。

const pricesAtom = atom({
apple: atom(15),
orange: atom(12),
pineapple: atom(25),
})
const Fruit = ({ name, priceAtom }) => {
const [price] = useAtom(priceAtom)
return (
<div>
{name}: {price}
</div>
)
}
const Parent = () => {
const [prices] = useAtom(pricesAtom)
return (
<div>
{Object.keys(prices).map((name) => (
<Fruit name={name} priceAtom={prices[name]} key={name} />
))}
</div>
)
}