React-Redux中dispatch触发后reducer执行全流程问询
Redux 状态流转全链路拆解(对应各节点问题说明)
1. 触发reducer执行的核心逻辑
真正触发reducer执行的逻辑是**createStore内部封装的原生dispatch闭包**,而非外部业务代码:
- 当通过
createStore(reducer, enhancer)创建Redux store时,Redux会把传入的reducer存储在store的闭包变量中,同时生成一个原生dispatch函数,这个函数的核心职责就是被调用时,读取闭包存储的当前状态、传入的action,将两个值作为入参传递给缓存的reducer执行 - 日常业务中调用的
store.dispatch、react-redux提供的useDispatch()返回的dispatch,大多是经过中间件链、store增强器包装后的版本,这些包装版本最终都会回溯到上述原生dispatch闭包,由它完成对reducer的直接调用
2. 从手动调用dispatch到reducer正式运行前的执行顺序
如果项目接入了中间件,从入口调用到reducer启动的完整前置流程按顺序执行:
- 第一步:完成action参数的合法性校验,检查action是否为纯对象、是否携带
type字段,不合法直接抛出错误 - 第二步:沿中间件链从外到内依次执行每个中间件的前置逻辑(即中间件函数中
next调用之前的代码块):比如redux-thunk会判断action是否为函数,如果是就拦截处理,不继续向下传递;redux-logger会在这一步打印旧状态、即将触发的action类型 - 第三步:所有中间件逻辑执行完成、走到最内层的原生dispatch入口时,才会正式准备调用
reducer
注:如果项目未接入任何中间件,跳过第二步,参数校验完成后就直接触发reducer执行
3. reducer返回新状态后的执行顺序
当reducer执行完成返回新的state对象后,后续同步执行的逻辑顺序固定为:
- 第一步:用
reducer返回的新state,替换store闭包里存储的currentState变量——这是所有后续读取新状态的基础 - 第二步:遍历所有通过
store.subscribe()注册的订阅回调函数,按注册顺序依次执行每个回调(Redux本身不会主动给回调传参,部分封装的订阅逻辑会主动读取最新状态传给回调) - 第三步:执行完所有订阅回调后,本次dispatch的同步调用栈结束
4. 生成的新状态的最终流转路径
更新后的state会流向三个核心方向:
- 长期存储在store的闭包变量中,所有调用
store.getState()的逻辑拿到的都是这个最新值 - 传递给所有通过subscribe注册的订阅回调:比如Redux DevTools的状态快照上传逻辑、react-redux绑定的组件订阅逻辑,都会接收到这个新状态
- 推入React的更新调度队列:react-redux的订阅逻辑拿到新状态后,会和对应组件之前订阅的状态片段做浅对比,如果检测到变化,就会把组件标记为需要重渲染,交给React的调度系统排队处理,不会立刻触发重渲染
5. reducer返回新状态后,相关Hook是否会同步返回更新值
答案是不会同步拿到更新,全程是异步调度的过程,两类Hook的表现分别是:
- 关于
useSelector:当reducer执行完、store更新状态、触发订阅回调的整个流程走完时,当前正在执行的代码栈里,useSelector返回的还是之前的旧值。只有等React把对应组件的重渲染任务排到执行点,重新运行组件渲染函数时,useSelector才会在执行过程中从store读取最新的状态值返回,这个时机是晚于dispatch同步执行流程的。新手容易踩的典型坑:在调用dispatch的下一行代码直接读取当前组件里useSelector返回的变量,拿到的一定是更新前的旧值,因为组件重渲染是在本次同步代码全部执行完成后才会被调度触发的 - 关于
useState:它和Redux没有任何内置的联动关系,只有当你手动在Redux的订阅回调里调用useState返回的更新函数时,才会触发对应的状态更新,这个更新同样要走React的调度队列,不会在reducer返回的当下同步把新值返回给useState。
内容的提问来源于stack exchange,提问作者Tips and Tricks Of Chess
相关产品推荐
相关产品推荐

