在渲染时初始化状态
有时,你可能需要创建一个使用原子状态的可复用组件。
这些原子的初始状态由传递给组件的 props 决定。
下面是一个基本的示例,说明如何使用 Provider
及其 prop initialValues
来初始化状态。
基本示例
CodeSandbox 链接:codesandbox。
考虑一个基本示例,你有一个可复用的 TextDisplay
组件,它允许你显示和更新纯文本。
这个组件有两个子组件,PrettyText
和 UpdateTextInput
。
PrettyText
以蓝色显示文本。UpdateTextInput
是一个输入字段,它更新文本值。
你决定将 text
状态作为原子在组件之间共享,而不是在两个子组件中传递 text
作为 prop。
为了使 TextDisplay
组件可复用,我们接受一个 prop initialTextValue
,它决定 text
原子的初始状态。
为了将 initialTextValue
与 textAtom
关联起来,我们将子组件包装在一个组件中,在该组件中我们创建一个新的存储并将其传递给 Provider
组件。
const textAtom = atom('')const PrettyText = () => {const [text] = useAtom(textAtom)return (<><textstyle={{color: 'blue',}}>{text}</text></>)}const UpdateTextInput = () => {const [text, setText] = useAtom(textAtom)const handleInputChange = (e) => {setText(e.target.value)}return (<><input onChange={handleInputChange} value={text} /></>)}const HydrateAtoms = ({ initialValues, children }) => {// 在这里使用 prop 在渲染时初始化状态useHydrateAtoms(initialValues)return children}export const TextDisplay = ({ initialTextValue }) => (<Provider><HydrateAtoms initialValues={[[textAtom, initialTextValue]]}><PrettyText /><br /><UpdateTextInput /></HydrateAtoms></Provider>)
现在,我们可以轻松地复用 TextDisplay
组件,尽管它们引用的是"相同"的原子,但可以使用不同的初始文本值。
export default function App() {return (<div className="App"><TextDisplay initialTextValue="initial text value 1" /><TextDisplay initialTextValue="initial text value 2" /></div>)}
这种行为是因为我们的子组件寻找最低的公共 Provider
祖先来获取其值。
有关 Provider
行为的更多信息,请阅读这里的文档。
对于更复杂的用例,请查看 Scope extension。
使用 Typescript
useHydrateAtoms
有重载类型,typescript 无法从重载函数中提取类型。建议在将初始原子值传递给 useHydrateAtoms
时使用 Map
。
以下是一个工作示例:
import type { ReactNode } from 'react'import { Provider, atom, useAtomValue } from 'jotai'import type { WritableAtom } from 'jotai'import { useHydrateAtoms } from 'jotai/utils'const testAtom = atom('')export default function App() {return (<Provider><AtomsHydrator atomValues={[[testAtom, 'hello']]}><Component /></AtomsHydrator></Provider>)}//这个组件包含所有的状态和逻辑function Component() {const testAtomValue = useAtomValue(testAtom)return <div>{testAtomValue}</div>}function AtomsHydrator({atomValues,children,}: {// eslint-disable-next-line @typescript-eslint/no-explicit-anyatomValues: Iterable<readonly [WritableAtom<unknown, [any], unknown>, unknown]>children: ReactNode}) {useHydrateAtoms(new Map(atomValues))return children}