对比#
Zustand 是 React 众多状态管理库中的一个。 本页将把 Zustand 与其中一些库进行对比, 包括 Redux、Valtio、Jotai 和 Recoil。
每个库都有自己的长处和短板, 我们会比较它们之间的关键差异与相似之处。
#Redux
#状态模型(对比 Redux)
从概念上讲,Zustand 和 Redux 非常相似, 两者都基于不可变的状态模型。 不过,Redux 需要用 context provider 包裹你的应用; Zustand 则不需要。
Zustand
import { create } from 'zustand'
type State = {
count: number
}
type Actions = {
increment: (qty: number) => void
decrement: (qty: number) => void
}
const useCountStore = create<State & Actions>((set) => ({
count: 0,
increment: (qty: number) => set((state) => ({ count: state.count + qty })),
decrement: (qty: number) => set((state) => ({ count: state.count - qty })),
}))import { create } from 'zustand'
type State = {
count: number
}
type Action = {
type: 'increment' | 'decrement'
qty: number
}
type Actions = {
dispatch: (action: Action) => void
}
const countReducer = (state: State, action: Action) => {
switch (action.type) {
case 'increment':
return { count: state.count + action.qty }
case 'decrement':
return { count: state.count - action.qty }
default:
return state
}
}
const useCountStore = create<State & Actions>((set) => ({
count: 0,
dispatch: (action: Action) => set((state) => countReducer(state, action)),
}))Redux
import { createStore } from 'redux'
import { useSelector, useDispatch } from 'react-redux'
type State = {
count: number
}
type Action = {
type: 'increment' | 'decrement'
qty: number
}
const countReducer = (state: State, action: Action) => {
switch (action.type) {
case 'increment':
return { count: state.count + action.qty }
case 'decrement':
return { count: state.count - action.qty }
default:
return state
}
}
const countStore = createStore(countReducer)import { createSlice, configureStore } from '@reduxjs/toolkit'
const countSlice = createSlice({
name: 'count',
initialState: { value: 0 },
reducers: {
incremented: (state, qty: number) => {
// Redux Toolkit does not mutate the state, it uses the Immer library
// behind scenes, allowing us to have something called "draft state".
state.value += qty
},
decremented: (state, qty: number) => {
state.value -= qty
},
},
})
const countStore = configureStore({ reducer: countSlice.reducer })#渲染优化(对比 Redux)
说到应用内的渲染优化, Zustand 和 Redux 的做法没有太大差别。 这两个库都建议你通过选择器手动进行渲染优化。
Zustand
import { create } from 'zustand'
type State = {
count: number
}
type Actions = {
increment: (qty: number) => void
decrement: (qty: number) => void
}
const useCountStore = create<State & Actions>((set) => ({
count: 0,
increment: (qty: number) => set((state) => ({ count: state.count + qty })),
decrement: (qty: number) => set((state) => ({ count: state.count - qty })),
}))
const Component = () => {
const count = useCountStore((state) => state.count)
const increment = useCountStore((state) => state.increment)
const decrement = useCountStore((state) => state.decrement)
// ...
}Redux
import { createStore } from 'redux'
import { useSelector, useDispatch } from 'react-redux'
type State = {
count: number
}
type Action = {
type: 'increment' | 'decrement'
qty: number
}
const countReducer = (state: State, action: Action) => {
switch (action.type) {
case 'increment':
return { count: state.count + action.qty }
case 'decrement':
return { count: state.count - action.qty }
default:
return state
}
}
const countStore = createStore(countReducer)
const Component = () => {
const count = useSelector((state) => state.count)
const dispatch = useDispatch()
// ...
}import { useSelector } from 'react-redux'
import type { TypedUseSelectorHook } from 'react-redux'
import { createSlice, configureStore } from '@reduxjs/toolkit'
const countSlice = createSlice({
name: 'count',
initialState: { value: 0 },
reducers: {
incremented: (state, qty: number) => {
// Redux Toolkit does not mutate the state, it uses the Immer library
// behind scenes, allowing us to have something called "draft state".
state.value += qty
},
decremented: (state, qty: number) => {
state.value -= qty
},
},
})
const countStore = configureStore({ reducer: countSlice.reducer })
const useAppSelector: TypedUseSelectorHook<typeof countStore.getState> = useSelector
const useAppDispatch: () => typeof countStore.dispatch = useDispatch
const Component = () => {
const count = useAppSelector((state) => state.count.value)
const dispatch = useAppDispatch()
// ...
}#Valtio
#状态模型(对比 Valtio)
Zustand 和 Valtio 在状态管理上的思路 有着根本性的不同。 Zustand 基于不可变状态模型, 而 Valtio 基于可变状态模型。
Zustand
import { create } from 'zustand'
type State = {
obj: { count: number }
}
const store = create<State>(() => ({ obj: { count: 0 } }))
store.setState((prev) => ({ obj: { count: prev.obj.count + 1 } }))Valtio
import { proxy } from 'valtio'
const state = proxy({ obj: { count: 0 } })
state.obj.count += 1#渲染优化(对比 Valtio)
Zustand 和 Valtio 的另一个区别在于, Valtio 通过属性访问来实现渲染优化。 而对 Zustand 来说,建议你 通过选择器手动进行渲染优化。
Zustand
import { create } from 'zustand'
type State = {
count: number
}
const useCountStore = create<State>(() => ({
count: 0,
}))
const Component = () => {
const count = useCountStore((state) => state.count)
// ...
}Valtio
import { proxy, useSnapshot } from 'valtio'
const state = proxy({
count: 0,
})
const Component = () => {
const { count } = useSnapshot(state)
// ...
}#Jotai
#状态模型(对比 Jotai)
Zustand 和 Jotai 之间有一个主要区别。 Zustand 是单一的 store, 而 Jotai 由可以组合在一起的基础原子(atom)构成。
Zustand
import { create } from 'zustand'
type State = {
count: number
}
type Actions = {
updateCount: (countCallback: (count: State['count']) => State['count']) => void
}
const useCountStore = create<State & Actions>((set) => ({
count: 0,
updateCount: (countCallback) => set((state) => ({ count: countCallback(state.count) })),
}))Jotai
import { atom } from 'jotai'
const countAtom = atom<number>(0)#渲染优化(对比 Jotai)
Jotai 通过原子依赖来实现渲染优化。 而对 Zustand 来说,建议你 通过选择器手动进行渲染优化。
Zustand
import { create } from 'zustand'
type State = {
count: number
}
type Actions = {
updateCount: (countCallback: (count: State['count']) => State['count']) => void
}
const useCountStore = create<State & Actions>((set) => ({
count: 0,
updateCount: (countCallback) => set((state) => ({ count: countCallback(state.count) })),
}))
const Component = () => {
const count = useCountStore((state) => state.count)
const updateCount = useCountStore((state) => state.updateCount)
// ...
}Jotai
import { atom, useAtom } from 'jotai'
const countAtom = atom<number>(0)
const Component = () => {
const [count, updateCount] = useAtom(countAtom)
// ...
}#Recoil
#状态模型(对比 Recoil)
Zustand 与 Recoil 的区别 和 Zustand 与 Jotai 的区别类似。 Recoil 依赖原子的字符串 key, 而不是原子对象的引用同一性。 此外,Recoil 需要用 context provider 包裹你的应用。
Zustand
import { create } from 'zustand'
type State = {
count: number
}
type Actions = {
setCount: (countCallback: (count: State['count']) => State['count']) => void
}
const useCountStore = create<State & Actions>((set) => ({
count: 0,
setCount: (countCallback) => set((state) => ({ count: countCallback(state.count) })),
}))Recoil
import { atom } from 'recoil'
const count = atom({
key: 'count',
default: 0,
})#渲染优化(对比 Recoil)
与前面几组渲染优化对比类似, Recoil 通过原子依赖来实现渲染优化。 而对 Zustand 来说,建议你 通过选择器手动进行渲染优化。
Zustand
import { create } from 'zustand'
type State = {
count: number
}
type Actions = {
setCount: (countCallback: (count: State['count']) => State['count']) => void
}
const useCountStore = create<State & Actions>((set) => ({
count: 0,
setCount: (countCallback) => set((state) => ({ count: countCallback(state.count) })),
}))
const Component = () => {
const count = useCountStore((state) => state.count)
const setCount = useCountStore((state) => state.setCount)
// ...
}Recoil
import { atom, useRecoilState } from 'recoil'
const countAtom = atom({
key: 'count',
default: 0,
})
const Component = () => {
const [count, setCount] = useRecoilState(countAtom)
// ...
}