用 props 初始化状态#
在需要依赖注入的场景中,例如要用组件传入的 props 来初始化 store 时,推荐的做法是使用原生 store 配合 React.context。
#用 createStore 创建 store
import { createStore } from 'zustand'
interface BearProps {
bears: number
}
interface BearState extends BearProps {
addBear: () => void
}
type BearStore = ReturnType<typeof createBearStore>
const createBearStore = (initProps?: Partial<BearProps>) => {
const DEFAULT_PROPS: BearProps = {
bears: 0,
}
return createStore<BearState>()((set) => ({
...DEFAULT_PROPS,
...initProps,
addBear: () => set((state) => ({ bears: ++state.bears })),
}))
}#用 React.createContext 创建上下文
import { createContext } from 'react'
export const BearContext = createContext<BearStore | null>(null)#组件的基本用法
// Provider implementation
import { useState } from 'react'
function App() {
const [store] = useState(() => createBearStore())
return (
<BearContext.Provider value={store}>
<BasicConsumer />
</BearContext.Provider>
)
}// Consumer component
import { useContext } from 'react'
import { useStore } from 'zustand'
function BasicConsumer() {
const store = useContext(BearContext)
if (!store) throw new Error('Missing BearContext.Provider in the tree')
const bears = useStore(store, (s) => s.bears)
const addBear = useStore(store, (s) => s.addBear)
return (
<>
<div>{bears} Bears.</div>
<button onClick={addBear}>Add bear</button>
</>
)
}#常见模式
#封装上下文 Provider
// Provider wrapper
import { useState } from 'react'
type BearProviderProps = React.PropsWithChildren<BearProps>
function BearProvider({ children, ...props }: BearProviderProps) {
const [store] = useState(() => createBearStore(props))
return <BearContext.Provider value={store}>{children}</BearContext.Provider>
}#把上下文逻辑抽取成自定义 Hook
// Mimic the hook returned by `create`
import { useContext } from 'react'
import { useStore } from 'zustand'
function useBearContext<T>(selector: (state: BearState) => T): T {
const store = useContext(BearContext)
if (!store) throw new Error('Missing BearContext.Provider in the tree')
return useStore(store, selector)
}// Consumer usage of the custom hook
function CommonConsumer() {
const bears = useBearContext((s) => s.bears)
const addBear = useBearContext((s) => s.addBear)
return (
<>
<div>{bears} Bears.</div>
<button onClick={addBear}>Add bear</button>
</>
)
}#可选:用记忆化选择器获得稳定的输出
import { useShallow } from 'zustand/react/shallow'
const meals = ['Salmon', 'Berries', 'Nuts']
function CommonConsumer() {
const bearMealsOrder = useBearContext(
useShallow((s) =>
Array.from({ length: s.bears }).map((_, index) => meals.at(index % meals.length)),
),
)
return (
<>
Order:
<ul>
{bearMealsOrder.map((meal) => (
<li key={meal}>{meal}</li>
))}
</ul>
</>
)
}#可选:允许使用自定义相等性函数
// Allow custom equality function by using useStoreWithEqualityFn instead of useStore
import { useContext } from 'react'
import { useStoreWithEqualityFn } from 'zustand/traditional'
function useBearContext<T>(
selector: (state: BearState) => T,
equalityFn?: (left: T, right: T) => boolean,
): T {
const store = useContext(BearContext)
if (!store) throw new Error('Missing BearContext.Provider in the tree')
return useStoreWithEqualityFn(store, selector, equalityFn)
}#完整示例
// Provider wrapper & custom hook consumer
function App2() {
return (
<BearProvider bears={2}>
<HookConsumer />
</BearProvider>
)
}