React 状态管理:从 Redux Toolkit 到 Jotai、Zustand等主流库中,应该如何在项目中进行选择。
本文系统梳理了React状态管理的演进路线。首先指出状态管理的核心是解决状态存放和更新问题,而非单纯使用某个库。文章提出四层渐进方案:1)简单场景优先使用useState/useRef管理局部状态;2)共享但不复杂时采用Jotai原子状态;3)状态模块化时选择Zustand组织store;4)复杂项目使用ReduxToolkit确保工程化规范。强调应根据项目实际复杂度选择工具,而非盲目引入状态库,
背景
在 React 开发里,状态管理(State Management,指“对组件数据、共享数据、更新逻辑进行统一组织和控制”)几乎是绕不开的话题。
很多初学者一开始会把“状态管理”理解成“必须使用一个库”,但这其实不准确。真正的问题不是“要不要用库”,而是:
当项目越来越复杂时,我们该如何管理状态,才能让代码保持清晰、可维护、可扩展。
所以这篇文章不只是讲几个库怎么用,更重要的是梳理一条合理的演进路线:
为什么会有状态管理这个问题
Redux Toolkit(Redux 官方工具集)为什么曾经成为主流方案
为什么很多场景下,根本不需要上库,先用
useState、useRef就够了当共享状态开始变多时,为什么 Jotai(原子状态管理库)很合适
当项目进一步复杂,需要把状态系统化组织时,为什么会引出 Zustand(轻量全局状态管理库)
一、为什么前端会遇到状态管理问题
先从最根本的问题说起。
React是组件化开发。组件内部有自己的数据,组件之间也会共享数据。如果项目小,这些数据通常很好处理;但随着业务增长,状态会越来越复杂,典型问题就会出现:
一个状态要传很多层组件(一对多)
多个组件依赖同一份数据(多对一)
更新逻辑分散在不同地方
异步请求和本地状态混在一起
数据变化越来越难追踪
这样就出现了两个核心矛盾点:
1. 状态放哪里
是放在某个组件里,还是提到父组件,还是做成全局共享?
2. 状态怎么改
是任意组件都能改,还是必须走统一流程?
所以,所谓“状态管理”,本质上解决的是两件事:
状态如何存放
状态如何更新
这也是后面 Redux Toolkit、Jotai、Zustand 这些方案的核心区别。
二、Redux Toolkit:最早成熟、最有体系的状态管理方案之一
如果说 React 状态管理历史上最经典、最有代表性的方案,Redux 一定绕不过去。
但今天真正应该学习的,不是早年那种样板代码很多的 Redux 老写法,而是 Redux Toolkit(Redux 官方工具集)。
Redux Toolkit 的本质,是在保留 Redux 思想的前提下,把 Redux 原本繁琐的写法进行官方简化。
所以理解 Redux Toolkit,先要理解 Redux 的核心思想。
2.1 Redux 的核心思想是什么
Redux 的核心思想可以概括成一句话:
把共享状态集中放进一个 store(状态仓库),所有状态更新都通过统一流程完成。
这套流程里有三个最关键的概念:
1. state(状态)
就是当前数据本身。
比如:
当前用户名称
当前购物车商品
当前计数器值
2. action(动作对象)
action(动作对象,指“描述发生了什么的普通对象”)不是直接修改状态的代码,而是一个“说明书”。
例如:
{ type: 'counter/increment' }
它的意思不是“立刻把 count +1”,而是:
发生了一个“计数增加”的动作
也就是说,action 负责描述状态要发生什么变化。
3. dispatch(分发函数)
dispatch(分发函数,指“把 action 发送到状态系统中的入口方法”)的作用,是把 action 交给 Redux。
比如:
dispatch({ type: 'counter/increment' })
它的意思是:
把“counter/increment 这个动作发生了”这件事,发送给 Redux 处理
所以它不是“修改状态”的代码,而是“发起状态变更流程”的入口。
4. reducer(归约函数)
reducer(归约函数,指“接收旧状态和 action,计算并返回新状态的纯函数”)才是真正负责“怎么改状态”的地方。
也就是说:
action 只负责描述
dispatch 只负责发送
reducer 负责根据描述,计算出新的 state
这三者的关系可以记成一句话:
action 是变更说明书,dispatch 是投递动作,reducer 是真正计算新状态的地方。
2.2 Redux 的运行机制
Redux 的运行机制其实非常清晰:
组件触发 dispatch(action)
-> store 收到 action
-> reducer 根据 action 和旧 state 计算新 state
-> store 保存新 state
-> React 组件读取到新 state,重新渲染
import { configureStore, createSlice } from '@reduxjs/toolkit'
const counterSlice = createSlice({
name: 'counter',
initialState: { count: 0 },
reducers: {
increment: (state) => {
state.count += 1
},
setCount: (state, action) => {
state.count = action.payload
},
},
})
export const { increment, setCount } = counterSlice.actions
const store = configureStore({
reducer: {
counter: counterSlice.reducer,
},
})
export default store
这段代码里有几个关键点:
createSlice(切片创建函数)
createSlice(切片创建函数,指“把一组相关状态和更新逻辑组织在一起的 Redux Toolkit API”)用来把一个模块的状态和 reducer 放在一起管理。
这里的 counterSlice 就是一个计数器模块。
initialState
initialState: { count: 0 }
表示这个模块的初始状态。
reducers
reducers: {
increment: (state) => {
state.count += 1
},
setCount: (state, action) => {
state.count = action.payload
},
}
这里定义的是这个模块允许怎样更新。
increment:把count加一
setCount:把count设置成外部传进来的值
这里的 action.payload(动作载荷,指“action 中携带的额外数据”)就是传进来的新值。
configureStore
const store = configureStore({
reducer: {
counter: counterSlice.reducer,
},
})
configureStore(仓库配置函数,指“创建 Redux store 并自动完成常见配置的 API”)用于创建全局 store。
2.3 Redux Toolkit 的价值是什么
Redux Toolkit 的价值,不是“发明了新的状态管理思想”,而是:
让 Redux 这套强约束、强组织性的思想,更容易写、更适合现代 React 项目。
它特别适合以下场景:
大型项目
团队多人协作
状态变更流程要清晰可追踪
异步逻辑比较多
希望有明确的模块边界
它最大的优点是:
1. 统一
所有人都按同一套流程改状态。
2. 可追踪
你能知道状态是被哪个 action 改掉的。
3. 可维护
复杂项目里,统一规范比“随手能写”更重要。
2.4 Redux Toolkit 的问题是什么
Redux Toolkit 很强,但它并不适合一切场景。
它的问题不是“不好”,而是:
对于很多中小项目来说,它可能有点重。
为什么?
因为它本质上仍然是一套“有组织、有约束”的状态系统。
当你的状态其实并不复杂时,直接上 Redux Toolkit 可能会带来这些问题:
概念较多
全局化太早
小需求也要走一整套流程
对简单项目来说心智负担偏大
所以,Redux Toolkit 很重要,但它不是“默认第一选择”。
它更像是:复杂项目里很稳的工程化方案。
三、什么时候其实不需要状态管理库:先考虑 useState 和 useRef
这是很多人最容易忽略的一步。
一提“状态管理”,很多人会立刻想到 Redux、Jotai、Zustand。
但真正成熟的做法是:
先判断这个状态是否真的需要共享,是否真的需要被管理。
很多状态,其实只属于当前组件本身,这时候最合适的方案,永远是 React 自带能力。
3.1 useState(状态 Hook)
useState(状态 Hook,指“React 中用于在函数组件里保存和更新局部状态的 Hook”)适合管理组件内部状态。
例如:
输入框内容
弹窗显示与隐藏
当前 tab
局部 loading
表单校验状态
代码示例:
import { useState } from 'react'
function Counter() {
const [count, setCount] = useState(0)
return (
<div>
<p>当前计数:{count}</p>
<button onClick={() => setCount(count + 1)}>+1</button>
</div>
)
}
这里的逻辑非常直接:
count是当前状态
setCount是更新函数
如果状态只在当前组件里用,这就是最合适的方案。
3.2 useRef(引用 Hook)
useRef(引用 Hook,指“用于保存不会触发组件重新渲染的可变引用值,或获取 DOM 引用的 Hook”)通常用于两类场景:
1. 获取 DOM 引用
import { useRef } from 'react'
function InputBox() {
const inputRef = useRef<HTMLInputElement | null>(null)
const focusInput = () => {
inputRef.current?.focus()
}
return (
<div>
<input ref={inputRef} />
<button onClick={focusInput}>聚焦输入框</button>
</div>
)
}
2. 保存不需要触发重新渲染的值
例如:
定时器 id
上一次的值
某些缓存数据
也就是说,useRef 更偏“引用”和“容器”,而不是用来驱动 UI 重新渲染。
3.3 为什么这一层最重要
因为很多状态,根本不值得全局化。
如果一个状态:
只在当前组件使用
不需要跨组件共享
没有复杂更新逻辑
那么最好的方案就是:
不要引入额外状态管理库。
这是状态管理里非常关键的一条原则:
状态尽量靠近使用它的地方。
换句话说:
不要为了“以后可能会复杂”而过早引入全局状态管理。
四、从局部状态走向共享状态:为什么会需要 Jotai
当项目继续增长,你会发现只靠 useState 已经开始吃力了。
比如:
多个组件需要共享一份状态
某个状态不只在父子组件中使用
props(属性传递)一层层往下传很麻烦
一些状态之间存在明显的依赖关系
这时我们就开始进入“共享状态”阶段。
而 Jotai(原子状态管理库)就是一个非常适合这个阶段的方案。
4.1 Jotai 的核心思想是什么
Jotai 的核心是 atom(原子状态,指“最小可独立读写的状态单元”)。
你可以把它理解成:
把原本组件内部的状态,拆成一个一个可共享的小状态单元。
这也是 Jotai 最大的特点:
它不是先让你建立一个很大的全局 store,而是让你从一个个 atom 开始。
所以它特别像 useState 的自然升级版。
4.2 为什么 Jotai 适合这个阶段
Jotai 适合的场景是:
状态开始需要共享
但项目还没复杂到必须建立完整 store 体系
你希望写法尽量贴近 React
希望状态粒度细一些
希望派生状态(可以理解为:它不是一个独立保存的数据,而是由已有数据推导出来的结果)表达清晰
换句话说:
Jotai 适合“共享了,但还没复杂到系统化管理”的状态。
4.3 Jotai 的基本用法
基础 atom
import { atom, useAtom } from 'jotai'
const countAtom = atom(0)
function Counter() {
const [count, setCount] = useAtom(countAtom)
return (
<div>
<p>计数:{count}</p>
<button onClick={() => setCount((c) => c + 1)}>+1</button>
</div>
)
}
这里要这样理解:
atom(0)
表示定义了一个原子状态,它的初始值是 0。
useAtom(countAtom)
useAtom(原子状态 Hook,指“用于读取和更新 atom 的 Hook”)和 useState 很像,返回:
当前值(value)
更新函数(setter)
所以这里 Jotai 的使用体验非常接近 React 原生状态。
4.4 Jotai 的关键优势:派生状态
Jotai 真正很强的一点,是派生状态。
例如:
import { atom, useAtom } from 'jotai'
const priceAtom = atom(100)
const quantityAtom = atom(2)
const totalAtom = atom((get) => {
return get(priceAtom) * get(quantityAtom)
})
这里的 totalAtom 不是手动存进去的值,而是根据别的 atom 算出来的。
其中:
get(读取函数,指“Jotai 在派生 atom 中提供的,用于读取其他 atom 当前值的函数”)是 Jotai 自动传进来的工具函数。
所以:
get(priceAtom)表示读取priceAtom当前值
get(quantityAtom)表示读取quantityAtom当前值
整段代码的意思是:
totalAtom的值,等于priceAtom和quantityAtom当前值的乘积
这样设计的好处是:
状态依赖关系非常清晰
不需要手动同步
某个 atom 变化时,派生 atom 自动重新计算
这就是 Jotai 很适合处理中小型共享状态的原因之一。
4.5 Jotai 的定位
所以,从项目演进角度看,Jotai 解决的是这样一个问题:
当局部状态已经不够,但又不想太早引入大而全的全局状态架构时,如何优雅地共享状态?
答案就是:
用 atom,把共享状态拆成细粒度的可组合单元。
五、当项目继续变复杂:为什么会引出 Zustand
继续往后走,项目再大一些,状态就不只是“共享”这么简单了。
这时候你会开始遇到新的问题:
状态越来越多
业务模块越来越明显
状态和操作逻辑需要放在一起
需要更清晰的组织方式
不希望 atom 到处散落
这个阶段,项目对状态管理的要求已经从“能共享”变成了:
能组织、能分模块、能维护。
这就是 Zustand(轻量全局状态管理库)出现的价值。
5.1 Zustand 的核心思想是什么
Zustand 的核心是 store(状态仓库,指“集中保存一组相关状态和更新方法的容器”)。
如果说:
Jotai 是从“状态单元”出发
那么 Zustand 是从“状态模块”出发
它更强调把一组相关状态和操作放在一起。
所以它更像一种轻量的 store 模式。
5.2 Zustand 适合什么阶段
Zustand 适合这些场景:
全局状态已经明显按业务模块存在
需要把状态和更新方法集中组织
希望代码结构比 Jotai 更整齐
不想上 Redux Toolkit 那么重的体系
想要轻量,但又要有明显的组织感
简单说:
Zustand 适合“共享状态已经开始成系统”的项目。
5.3 Zustand 的基本用法
import { create } from 'zustand'
type CounterStore = {
count: number
increment: () => void
setCount: (count: number) => void
}
const useCounterStore = create<CounterStore>((set) => ({
count: 0,
increment: () =>
set((state) => ({
count: state.count + 1,
})),
setCount: (count) => set({ count }),
}))
这个例子里,有两个非常关键的写法。
第一种:依赖旧状态时,用函数形式
increment: () =>
set((state) => ({
count: state.count + 1,
}))
这里为什么要写成函数?
因为新状态依赖旧状态。
你需要先拿到原来的 state.count,再算出新的值。
也就是:
基于旧状态计算新状态
同样的也可以写成(对 state 进行解构):
increment: () =>
set(({ count }) => ({
count: count + 1,
}))
第二种:不依赖旧状态时,直接传对象
setCount: (count) => set({ count })
这里不需要看旧状态,因为你已经知道新值就是传进来的 count。
这段代码等价于:
setCount: (count) => set({ count: count })
只是因为对象属性名和变量名相同,所以可以简写成:
{ count }
5.4 Zustand 为什么比 Jotai 更“有组织”
Jotai 的思路是把状态拆成 atom。
Zustand 的思路是把相关状态和操作收进一个 store。
所以在项目更复杂时,Zustand 往往会显得更顺手,因为它天然更适合这种结构:
用户模块一个 store
购物车模块一个 store
权限模块一个 store
主题模块一个 store
这就比“很多 atom 分散定义”更容易建立模块边界。
也正因为这样,Zustand 常常被认为比 Jotai 更适合做“有组织的全局状态管理”。
六、把这条路线串起来:状态管理应该怎么选
现在把整篇文章的逻辑合起来,你就会发现,React 状态管理并不是“几个库谁更强”的问题,而是一个随着项目复杂度逐步演进的问题。
第一层:先用 React 原生能力
如果状态只是局部使用:
useState
useRef
优先解决。
这时候不要急着全局化。
第二层:开始共享,但还不复杂,用 Jotai
如果多个组件要共享状态,但项目还没到“强组织、强架构”的程度,Jotai 非常合适。
因为它:
足够轻
足够直观
和 React 心智接近
派生状态很好写
第三层:状态越来越多,需要组织,用 Zustand
如果状态不再只是零散共享,而是开始按业务模块成型,那么 Zustand 会更适合。
因为它强调:
store 化
模块化
状态和方法集中管理
第四层:复杂业务、多人协作、强规范,用 Redux Toolkit
当项目进入更复杂阶段,比如:
业务流程很长
状态更新要严格可追踪
团队多人协作
需要统一规范和调试能力
Redux Toolkit 依然是非常稳定的选择。
它不是最轻的,但通常是最“工程化”的。
结语:真正重要的不是库,而是状态管理思维
最后做一个总结。
前端状态管理的发展,不是为了让我们记住越来越多的 API,而是为了回答一个始终不变的问题:
当数据越来越多、组件越来越多、共享越来越频繁时,我们该怎样把状态放对地方、改对方式。
所以一条比较成熟的路线应该是:
先学会用
useState、useRef管好局部状态再理解 Redux Toolkit 的统一状态流思想
然后在实际项目中,根据复杂度选择 Jotai 或 Zustand
真正做到“工具匹配问题”,而不是“为了用库而用库”
这才是理解 React 状态管理的关键。
更多推荐



所有评论(0)