以下是针对零基础学习者的 Redux 知识点详细笔记,包含核心概念、工作原理、基础实现和 Redux Toolkit 的简化用法,帮助您从零开始理解 Redux 的完整流程。
一、为什么需要 Redux?
1. 问题背景
- 组件间状态共享困难:当多个组件需要访问或修改同一状态时,通过 props 逐层传递会非常繁琐(“prop drilling”)。
- 状态管理混乱:随着应用复杂度的增加,状态分散在不同组件中,难以维护和调试。
- 可预测性差:状态变化没有统一的规则,容易导致难以追踪的 bug。
2. Redux 的核心思想
- 集中管理状态:将所有状态存储在唯一的 Store 中。
- 单向数据流:状态只能通过预定义的规则(Reducer)修改,确保可预测性。
- 组件按需订阅:组件只订阅与自己相关的状态,减少不必要的渲染。
二、Redux 核心概念
1. 三大核心要素
名称 | 作用 | 类比解释 |
---|---|---|
Store | 存储全局状态的容器 | 相当于应用的“数据库” |
Action | 描述状态变化的普通对象(包含 type 和可选的 payload ) | 相当于“操作指令”(如“存钱”) |
Reducer | 纯函数,接收当前状态和 Action,返回新状态(不可直接修改原状态) | 相当于“银行柜员处理指令” |
2. Redux 三大原则
- 单一数据源:整个应用的状态存储在唯一 Store 中。
- 状态只读:唯一修改状态的方式是派发 Action。
- 纯函数修改:Reducer 必须是纯函数,无副作用,不可修改原状态。
三、Redux 基础实现(无 Redux Toolkit)
1. 手动实现一个计数器
(1) 定义 Action Types
const INCREMENT = 'INCREMENT';
const DECREMENT = 'DECREMENT';
(2) 定义 Action Creators
const increment = () => ({ type: INCREMENT });
const decrement = () => ({ type: DECREMENT });
(3) 定义 Reducer
const initialState = { count: 0 };
function counterReducer(state = initialState, action) {
switch (action.type) {
case INCREMENT:
return { ...state, count: state.count + 1 }; // ✅ 不可变更新
case DECREMENT:
return { ...state, count: state.count - 1 };
default:
return state;
}
}
(4) 创建 Store
import { createStore } from 'redux';
const store = createStore(counterReducer);
(5) 组件中使用
// 订阅状态变化
store.subscribe(() => {
console.log('Current state:', store.getState());
});
// 派发 Action
store.dispatch(increment()); // { count: 1 }
store.dispatch(decrement()); // { count: 0 }
四、Redux Toolkit 简化实现
1. Redux Toolkit 核心工具
工具 | 作用 |
---|---|
configureStore | 简化 Store 创建(自动集成中间件、DevTools) |
createSlice | 自动生成 Reducer 和 Action(减少样板代码) |
createAsyncThunk | 处理异步操作(如 API 请求) |
2. 用 Redux Toolkit 重构计数器
(1) 创建 Slice
// counterSlice.js
import { createSlice } from '@reduxjs/toolkit';
const counterSlice = createSlice({
name: 'counter',
initialState: { count: 0 },
reducers: {
increment: (state) => {
state.count += 1; // ✅ 直接修改(内部使用 Immer 处理不可变性)
},
decrement: (state) => {
state.count -= 1;
},
},
});
export const { increment, decrement } = counterSlice.actions;
export default counterSlice.reducer;
(2) 创建 Store
// store.js
import { configureStore } from '@reduxjs/toolkit';
import counterReducer from './counterSlice';
export const store = configureStore({
reducer: {
counter: counterReducer,
},
});
(3) 在 React 组件中使用
import { useSelector, useDispatch } from 'react-redux';
import { increment, decrement } from './counterSlice';
function Counter() {
const count = useSelector((state) => state.counter.count);
const dispatch = useDispatch();
return (
<div>
<button onClick={() => dispatch(decrement())}>-</button>
<span>{count}</span>
<button onClick={() => dispatch(increment())}>+</button>
</div>
);
}
五、Redux 数据流详解
1. 流程图
组件 → dispatch(Action) → Store → Reducer → 更新 State → 通知订阅者 → 组件更新
2. 分步解释
- 组件触发事件(如点击按钮)→ 调用
dispatch(action)
。 - Store 接收 Action → 调用对应的 Reducer。
- Reducer 根据 Action 类型 → 返回新的状态。
- Store 更新状态 → 通知所有订阅了该状态的组件。
- 组件重新渲染 → 使用新状态更新 UI。
六、异步操作(使用 createAsyncThunk
)
1. 处理 API 请求
// postsSlice.js
import { createSlice, createAsyncThunk } from '@reduxjs/toolkit';
import axios from 'axios';
// 定义异步 Action
export const fetchPosts = createAsyncThunk('posts/fetchPosts', async () => {
const response = await axios.get('https://jsonplaceholder.typicode.com/posts');
return response.data;
});
const postsSlice = createSlice({
name: 'posts',
initialState: { data: [], loading: false, error: null },
extraReducers: (builder) => {
builder
.addCase(fetchPosts.pending, (state) => {
state.loading = true;
})
.addCase(fetchPosts.fulfilled, (state, action) => {
state.loading = false;
state.data = action.payload;
})
.addCase(fetchPosts.rejected, (state, action) => {
state.loading = false;
state.error = action.error.message;
});
},
});
export default postsSlice.reducer;
2. 组件中触发异步请求
import { useEffect } from 'react';
import { useDispatch, useSelector } from 'react-redux';
import { fetchPosts } from './postsSlice';
function PostsList() {
const dispatch = useDispatch();
const { data, loading, error } = useSelector((state) => state.posts);
useEffect(() => {
dispatch(fetchPosts());
}, [dispatch]);
if (loading) return <div>Loading...</div>;
if (error) return <div>Error: {error}</div>;
return (
<ul>
{data.map((post) => (
<li key={post.id}>{post.title}</li>
))}
</ul>
);
}
七、项目结构组织
推荐结构
src/
├── app/
│ └── store.js // Store 配置
├── features/ // 按功能模块划分
│ ├── counter/ // 计数器模块
│ │ ├── counterSlice.js
│ │ └── Counter.jsx
│ └── posts/ // 文章模块
│ ├── postsSlice.js
│ └── PostsList.jsx
└── index.js // 应用入口
八、常见问题解答
1. Redux 必须用于所有项目吗?
- 小型项目:使用 React 的
useState
或 Context API 即可。 - 中大型项目:Redux 更适合管理复杂状态和异步逻辑。
2. 直接修改状态为什么合法?
Redux Toolkit 内部使用 Immer 库,允许在 Reducer 中直接修改 state
,但最终会生成一个全新的不可变对象。
3. 如何调试?
- 安装 Redux DevTools 浏览器扩展 → 自动集成到 Redux Toolkit 的 Store。
- 查看 Action 派发记录和状态变化历史。
九、总结
Redux 核心流程
- 定义状态 → 创建 Store。
- 派发 Action → 描述状态变化。
- Reducer 处理 → 返回新状态。
- 组件订阅 → 按需更新 UI。
Redux Toolkit 优势
- 代码简洁:减少 70% 的样板代码。
- 开箱即用:集成中间件、DevTools、Immer。
- 异步友好:
createAsyncThunk
简化异步逻辑。
通过掌握这些核心概念,您已经能够使用 Redux 和 Redux Toolkit 构建可预测、易维护的状态管理系统!