工作流
React-Redux 的工作流是单向数据流的典型实现,核心围绕 Store(状态容器)、Reducer(状态计算逻辑)、Action(状态变更指令) 和 React 组件(视图层) 的协作展开。
一、初始状态初始化
Redux 应用的核心是一个 单一 Store(通过 createStore 或 configureStore 创建),它存储整个应用的状态树。
- 初始化时,Redux 会调用 Root Reducer(通常由
combineReducers合并多个子 Reducer),传入初始状态(initialState),生成 Store 的初始状态 - React 组件通过 Provider 组件(来自 React-Redux)包裹,将 Store 注入到组件树中,使所有子组件可访问
二、组件触发 Action (事件起点)
用户在界面进行交互(如点击按钮、输入表单)时, React 组件会 派发(Dispatch)一个 Action,这是状态变更的起点。
关键角色:
- Action: 一个普通 JavaScript 对象,必须包含 type 字段(标识操作类型),可选携带 payload (数据负载)
- Action Creator: 函数,用于生成 Action 对象(可选,但推荐使用,简化重复代码)
// 创建 Action
const increment = amount => ({ type: 'INCREMENT', payload: amount });
// 在组件中触发
dispatch(increment(1));
组件中的触发方式:
- 类组件:通过
connect高阶组件注入dispatch方法,直接调用this.props.dispatch(increment(1)) - 函数组件:通过
useDispatchHook获取dispatch函数,调用dispatch(increment(1))
三、 Reducer 计算新状态(纯函数处理)
Store 接收到 dispatch(action) 后,会将当前状态(currentState )和 Action 传递给 Root Reducer,由 Reducer 计算新状态。
Reducer 的核心规则:
- 纯函数:必须返回新的状态对象(不可修改原状态),无副作用(不调用 API、不修改外部变量)
- 状态不可变:通过复制原状态并修改副本的方式生成新状态(如使用展开运算符
...或 immer 库)
// 初始状态
const initialState = { count: 0 };
// Counter Reducer
function counterReducer(state, action) {
switch (action.type) {
case 'INCREMENT':
return { ...state, count: state.count + action.payload }; // 返回新状态
case 'DECREMENT':
return { ...state, count: state.count - action.payload };
default:
return state; // 未匹配的 Action 返回原状态
}
}
多 Reducer 合并:
复杂应用通常拆分为多个子 Reducer (如 userReducer、cartReducer),通过 combineReducers 合并成为 Root Reducer:
import { combineReducers } from 'redux';
const rootReducer = combineReducers({
user: userReducer,
cart: cartReducer,
// ... 其他 Reducer
});
四、 更新 Store 状态
Reducer 返回新状态后, Store 会:
- 替换当前状态为新的状态树(
currentState = newState) - 触发订阅通知:调用所有通过
store.subscribe(listener)注册的监听函数(React-Redux 会自动处理这部分)
五、 组件订阅并重新渲染(视图更新)
React 组件通过 订阅 Store 状态变化来感知更新,并重新渲染以后反映最新状态。
订阅的两种方式:
- 类组件:
connect高阶组件 - 函数组件:useSelectorHook 使用
1. 类组件: connect 高阶组件注入
connect(mapStateToProps, mapDispatchToProps)(myComponent) 会将组件与 Store 连接:
mapStateToProps:从 Store 状态中提取需要的属性(state => ({count: state.counter.count})),当相关状态变化时,组件重新渲染mapDispatchToProps:将 Action Creator 映射为组件的dispatch方法(可选,也可以手动调用dispatch)
2. 函数组件:useSelectorHook
useSelector(selector) 允许函数组件直接从 Store 中选择状态:
selector是一个函数(state => state.counter.count),返回组件需要的状态片段- 当
selector返回的值变化时,组件重新渲染(默认浅比较,可通过第二参数自定义比较逻辑)
六、异步 Action (如 API 请求)
实际开发中常需处理异步请求(如请求数据)。此时需借助中间件(Middleware),例如 redux-thunk 或 redux-saga
- 异步 Action Creator 返回一个函数(而非对象),通过
dispatch分阶段派发同步 Action (如 LOADING、SUCCESS/FAILURE)
const fetchUser = useId => async dispatch => {
dispatch({ type: 'USER_LOADING' }); // 开始加载
try {
const response = await api.getUser(userId);
dispatch({ type: 'USER_SUCCESS', payload: response.data }); // 加载成功
} catch (error) {
dispatch({ type: 'USER_FAILURE', payload: error.message }); // 加载失败
}
};
七、 完整的示例
- 定义 Action
- 定义 Reducer
- 定义 Store
- 组件
todoAction.js
const addToDo = text => ({
type: 'ADD_TODO',
payload: { text, id: Date.now() },
});
todoReducer.js
function todosReducer(state, action) {
switch(action.type) {
case: "ADD_TODO":
return [...state, action.payload];
default:
return state;
}
}
store.js
import { createStore } from 'redux';
import { todosReducer } from './todoReducer.js';
const store = createStore(todosReducer);
export { store };
Todo.mdx
import { useDispatch, useSelector } from 'react-redux';
import { addTodo } from './todoAction.js';
function TodoApp() {
const todos = useSelector(state => state);
const dispatch = useDispatch();
const handleAdd = () => {
dispatch(addTodo('新项'));
};
return (
<div>
<button onClick={handleAdd}>增加代办</button>
<ul>
{todos.map(todo => (
<li key={todo.id}>{todo.text}</li>
))}
</ul>
</div>
);
}