react结合redux实战|原理篇
30 秒速记
- 核心判断:状态管理的核心是把状态变化约束成可追踪的数据流,中间件在派发边界扩展异步、日志和副作用
- 原理主线:围绕 「一、定义constants」、「二、定义actionCreator」、「三、定义reducer」 建立输入、状态变化与输出之间的因果关系
- 文章范围:讲解了如何在React项目中结合Redux进行状态管理,通过TODO应用实例,介绍了常量、action creator、reducer等核心概念及实现步骤,帮助开发者掌握React与Redux集成的实战方法。
- 边界与代价:集中式状态不是越多越好;可变性、订阅粒度和异步取消决定性能与可维护性
- 工程落地:选型要从状态归属、更新频率、调试需求和团队约束出发,而不是只比较 API 数量
React 结合 Redux 的关键,是让组件只负责交互和展示,把状态变化统一交给 action、reducer 与 store。 用户操作经 dispatch 发出 action,reducer 根据类型返回新状态,connect 再把所需状态和操作映射成组件属性。以 TODO 为例,列表和筛选条件可拆成两个 reducer,再用 combineReducers 合并。日志等能力放在中间件层,展示组件本身不必直接管理这些数据。
这篇文章不要按 API 清单来背。先用上面的 Mind Map 建立全局结构,再通过交互 DEMO 观察正常路径和边界路径如何改变状态;阅读正文时重点核对每一步的输入、负责执行的参与者、产生的中间状态以及最终可观察结果。遇到版本敏感结论,要把“历史实现”“当前行为”和“工程兼容策略”分开说明;遇到性能或架构取舍,则用实际指标、失败现象和验证手段支撑判断。
版本校准: 本文若分析 ReactDOM.render、旧生命周期或栈调和,应把它视为理解架构演进的历史路径。React 19 已移除 ReactDOM.render,当前客户端入口使用 createRoot;并发渲染也必须区分可中断的渲染阶段与同步提交阶段。迁移前对照 React 19 官方升级指南。
以
TODO为例分析,实际开发中并不是那么简单,下面的原型只是开发中的一个原型,这个简单的例子,有助于掌握数据处理传递的原则。
# 一、定义constants
这一步不是必须的
/**
* 常量统一保存,便于管理
*/
export const ADD_TODO = 'ADD_TODO';
export const TOGGLE_TODO = 'TOGGLE_TODO';
export const SET_VISIBILITY = 'SET_VISIBILITY';
//controll todo wheher show or hide
export const SHOW_ALL = 'SHOW_ALL';
export const SHOW_ACTIVE = 'SHOW_ACTIVE';
export const SHOW_COMPLETED = 'SHOW_COMPLETED';
# 二、定义actionCreator
/**
* 定义action creator
*/
import * as actionType from '../constant/index';
let nextTodo = 0;
export const addTodo = (text)=>({
type:actionType.ADD_TODO,
id:nextTodo++,
text
})
export const toggleTodo = (id)=>({
type:actionType.TOGGLE_TODO,
id
})
export const setVisibilityFilter = (filter)=>{
return {
type:actionType.SET_VISIBILITY,
filter
}
}
# 三、定义reducer
拆分reducer
SetVisibility.js
/**
* 处理TODO可见与不可见的reducer
*/
import * as actionType from '../constant/index';
// 初始状态是自己设置的 后面的状态会转化
// 接收当前状态(设置默认的过滤SHOW_ALL,如设置某些选项卡的active一样),和action返回新的state
export const visibilityFilter = (state='SHOW_ALL',action)=>{
switch(action.type) {
case actionType.SET_VISIBILITY:
return action.filter;
default:
return state;
}
}
addTodo.js
/**
* 定义处理action的reducers
*/
import * as actionType from '../constant/index';
//传入当前的状态空数、action
export const todos = (state = [],action)=>{
switch(action.type){ // 匹配用户触发的actionType
case actionType.ADD_TODO:
// 合并上一次的状态和当前的状态 返回todos数组
return [
...state,//把数组展开合并
{
id:action.id,
text:action.text,
completed:false//用户控制TODO是否处于点击完成的状态 默认false 没点击
}
]
// TODO列表来回切换 遍历add_todo返回的数组 通过completed来判断
case actionType.TOGGLE_TODO:
return state.map(todo=>(todo.id===action.id)?{...todo,completed:!todo.completed}:todo)
default:
return state;//匹配不到返回state
}
}
合并reducer
/**
* 合并reducers
*/
import { combineReducers } from 'redux'
import {todos} from './addTodo';
import {visibilityFilter} from './SetVisibility';
export default combineReducers({
todos, //这些键其实就是被拆分的状态,后面在容器组件中需要通过connect链接
visibilityFilter
});
# 四、定义store
import { applyMiddleware, createStore } from 'redux';
import reducer from '../reducers/index';
import logger from 'redux-logger';
// 创建store 用来存储状态
export const store = createStore(
reducer,
applyMiddleware(logger) //处理日志中间件
)
# 五、结合react-redux
这里忽略展示组件,完成源码看文章结尾
- 首先我们在
container组件中处理好之前分解合并的那些reducer的键,然后在通过connect链接,传递给展示组件的属性使用
容器组件处理
react-todos/src/container/FilterLink.js
// 处理数组过滤
import { connect } from 'react-redux';
import Link from '../components/Link';
import { setVisibilityFilter } from '../actions/index';
// 这里的ownProps指的是 FilterLink
// 这里的state其实就是之前分解的todos,visibilityFilter
const mapStateToProps = (state, ownProps) => {
return {
active:ownProps.filter === state.visibilityFilter
}
}
/**
如果mapDispatchToProps是一个函数,会得到dispatch和ownProps(容器组件的props对象)两个参数。
*/
const mapDispatchToProps = (dispatch, ownProps) => ({
// 这里处理对应的事件,传递给展示组件的属性
onClick: () => {
dispatch(setVisibilityFilter(ownProps.filter))
}
})
export default connect(
mapStateToProps,
mapDispatchToProps
)(Link);
- 分析
- 这里通过
connect组件把之前reducer处理的那些状态链接
- 这里通过
export default combineReducers({
todos, //这些键其实就是被拆分的状态,后面在容器组件中需要通过connect链接
visibilityFilter
});
react-todos/src/container/VisibilityTodoList.js
← 浅析中间件Immutable总结 →
