JotaiJotai

Jotai
React 原始而灵活的状态管理

Atoms in atom

atom() 创建一个原子配置,这是一个对象,但它不持有值。 原子配置没有字符串键,我们通过引用相等性来识别它们。 换句话说,我们可以像使用键一样使用原子配置。

在 useState 中存储原子配置

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

const Component = ({ atom1, atom2 }) => {
const [selectedAtom, setSelectedAtom] = useState(atom1)
const [value] = useAtom(selectedAtom)
return (
<div>
选中的值:{value}
<button onClick={() => setSelectedAtom(atom1)}>选择一个原子</button>
<button onClick={() => setSelectedAtom(atom2)}>
选择另一个原子
</button>
</div>
)
}

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

这可能没有任何意义,但我们可以按需创建一个原子配置。

const Component = () => {
const [currentAtom, setCurrentAtom] = useState(() => atom(0))
const [count, setCount] = useAtom(currentAtom)
return (
<div>
计数:{count} <button onClick={() => setCount((c) => c + 1)}>+1</button>
<button onClick={() => setCurrentAtom(atom(0))}>创建新的</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}
<button onClick={() => setNameAtom(firstNameAtom)}>
显示名字
</button>
<button onClick={() => setNameAtom(lastNameAtom)}>显示姓氏</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}>添加</button>
</div>
)
}

这种方法的好处是,如果你增加一个计数, 只有对应的 Counter 组件重新渲染,其他组件不会重新渲染。

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

TypeScript 用户的提示

<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>
)
}