一个轻量级状态管理库
Store
一个包含应用状态的对象,每当状态改变时,订阅这个 store 的组件会自动重新渲染
刷新页面,状态会消失
创建 Store
使用 create 函数创建 store:
import { create } from 'zustand'
const useStore = create((set, get, store) => ({
// 第一层状态:
bears: 0, // 状态数据
// 下面的函数也叫 Action
increasePopulation: () => set((state) => ({ bears: state.bears + 1 })), // 根据先前的 state 设置新 state
removeAllBears: () => set({ bears: 0 }), // 直接设置新 state
updateBears: (newBears) => set({ bears: newBears }), // 根据入参设置新 state
}))最好将状态数据和它对应的 Action 操作放在一个 store 内部
create 函数
传入一个函数
函数的参数是:
set:用来更新状态,会与 store 中的现有 state 状态的第一层进行浅合并,state 必须要当作不可变数据进行处理,get:调用get()可以得到 state 里的所有内容,用于在set之外拿到 statestore:提供底层的 API 调用功能,高级用法
函数返回值:
- 一个带有 API 工具的 React Hook,包括
setState、getState、getInitialState和subscribe
嵌套 state 对象:
zustand 只会将 state 的第一层进行浅合并,如果要 set 深层对象:
1.使用扩展运算符
import { create } from 'zustand'
const useStore = create((set) => ({
nested: {
count: 0
},
inc: () =>
set((state) => ({
nested: {
...state.nested,
count: state.nested.count + 1
},
})),
}))2.使用 Immer 中间件
先安装 immer 库
import { create } from 'zustand'
const useStore = create(
immer((set) => ({
nested: {
count: 0
},
inc: () =>
set((state) => { // 注意:不用返回一个对象了
state.nested.count += 1 // 以看似“可变”的方式来编写状态更新逻辑
}),
}))
)异步操作
可以直接在状态对象中添加一个异步函数,这个函数可以使用 set 函数来更新状态:
import { create } from 'zustand'
const useStore = create((set, get, store) => ({
fetchData: async () => {
const response = await fetch('/api/xxx')
const items = await response.json()
set({ items })
}
}))重置 store 内的所有状态为初始值
const useSomeStore = create((set, get, store) => ({
// your code here
reset: () => {
set(store.getInitialState())
},
}))组件中使用
读取
通过 useStore 读取状态:
function BearCounter() {
const { bears } = useStore() // 读取对应的 state
return <h1>{bears} bears around here...</h1> // 使用 state
}性能优化
1.使用 selector 选择性读取单个值
如果一个组件直接使用 useStore() 读取了所有 state 值,但是只用到其中的部分值,这时候没有用到的值发生变化时组件也会重渲染
function BearCounter() {
const bears = useStore((state) => state.bears) // 使用 selector 读取对应的 state
return <h1>{bears} bears around here...</h1> // 使用 state
}或者封装成 selector hook:
import { create } from 'zustand'
const useStore = create((set, get, api) => ({
bears: 0,
}))
export const useBears = () => useStore((state) => state.bears) // selector hook组件中使用:
function BearCounter() {
const bears = useBears() // 使用 selector 读取对应的 state
return <h1>{bears} bears around here...</h1> // 使用 state
}2.使用 useShallow 防止重复渲染
const useStore = create((set) => ({
name: 'Alice',
age: 25,
}))如果确实要同时读取多个状态值:
// 组件中
const { name, age } = useStore(state => ({ name: state.name, age: state.age }))这里的关键问题是:
state => ({ name: state.name, age: state.age })这个选择器函数在每次组件渲染时都会执行,并返回一个全新的对象;- 即使
state.name和state.age的值没有变化,但这个新对象的引用地址和上一次是不同的; - 这会导致组件在任何 Store 状态变化时都重新渲染。
使用 useShallow:
原理:新旧对象的所有顶层属性都严格相等,那么 shallow 认为这两个对象是相等的,组件不会重新渲染
// 组件中
import { useShallow } from 'zustand/react/shallow'
const { name, age } = useStore(useShallow(state => ({ name: state.name, age: state.age })))subscribe 监听状态变更
store 中的数据变了执行对应函数
// 组件中
useEffect(() => {
// 1.订阅整个 store 的变化
const unsubscribe = useStore.subscribe(
(state, previousState) => {
console.log('状态变化了:', previousState, '→', state)
},
)
// 2.订阅 bears 的变化
const unsubscribe = useStore.subscribe(
(state) => state.bears,
// bears 变化后触发
(newV, oldV) => {
console.log(`bears 从 ${oldV} 变为 ${newV}`)
}
)
return unsubscribe
}, [])状态与 URL 的 hash 同步
把某些状态存在 url 的 hash 中,可以实现:
- 刷新页面不丢失
- 分享出去的链接包含当前页面的完整状态
- 可以收藏特定状态的页面作为书签
技术步骤:
- 创建一个 store,存放某个 state 状态
- 写同步逻辑:当 state 变化:
- 把 state 写到
location.hash - 当用户前进/后退/刷新 → 监听
window.onhashchange事件,把 hash 里的 state 读回来
- 把 state 写到
持久化
使用 persist 中间件