主题
使用 props 初始化状态
¥Initialize state with props
在需要 依赖注入 的情况下,例如当应使用组件中的 props 初始化存储时,建议的方法是使用带有 React.context 的 vanilla 存储。
¥In cases where dependency injection is needed, such as when a store should be initialized with props from a component, the recommended approach is to use a vanilla store with React.context.
使用 createStore
的存储创建者
¥Store creator with createStore
ts
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
创建上下文
¥Creating a context with React.createContext
ts
import { createContext } from 'react'
export const BearContext = createContext<BearStore | null>(null)
基本组件用法
¥Basic component usage
tsx
// Provider implementation
import { useRef } from 'react'
function App() {
const store = useRef(createBearStore()).current
return (
<BearContext.Provider value={store}>
<BasicConsumer />
</BearContext.Provider>
)
}
tsx
// 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>
</>
)
}
常见模式
¥Common patterns
封装上下文提供程序
¥Wrapping the context provider
tsx
// Provider wrapper
import { useRef } from 'react'
type BearProviderProps = React.PropsWithChildren<BearProps>
function BearProvider({ children, ...props }: BearProviderProps) {
const storeRef = useRef<BearStore>()
if (!storeRef.current) {
storeRef.current = createBearStore(props)
}
return (
<BearContext.Provider value={storeRef.current}>
{children}
</BearContext.Provider>
)
}
将上下文逻辑提取到自定义钩子中
¥Extracting context logic into a custom hook
tsx
// 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)
}
tsx
// 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>
</>
)
}
可选择允许使用自定义相等函数
¥Optionally allow using a custom equality function
tsx
// 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)
}
完整示例
¥Complete example
tsx
// Provider wrapper & custom hook consumer
function App2() {
return (
<BearProvider bears={2}>
<HookConsumer />
</BearProvider>
)
}