• 简体中文
  • 对比

    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)
      // ...
    }

    npm 下载趋势