从 redux 源码中探究 reducer 如何 “神奇的” 变换 state
Redux 作为现代前端实现数据流的一个核心库,其“神奇”的 state 更新机制成为了许多前端开发者学习的重点。在 Redux 中,分拆数据逻辑和业务逻辑的做法被称为“分离式”架构,其中最重要的组件是 reducer,即接收旧的 state 和 action,返回新的 state 的函数。
那么,我们如何理解 reducer 神奇的 state 变换机制呢?本文通过解析 Redux 源码,探究 reducer 的具体实现过程,希望能够给前端开发者带来更深入的认识和更实用的学习指导。
1、Redux 状态管理的基本原则
在 Redux 中,整个应用的 state 被存储在一个单一的 store 中,而 reducer 是这个 store 中捕捉变化的唯一途径。这意味着,该 state 的更新是根据一个简单的、确定的规则来实现的,即 reducer 中接收的 action 类型与数据类型决定了如何更新 state。
例如,我们可以定义一个 reducer,当接收到 ADD_TODO action 时,在 state.todos 数组中添加一个新的 todo,该 reducer 代码如下:
-------- ------------------ - --- ------- - ------ ------------- - ---- ----------- ------ - --------- - --- ----------- ----- ------------ ---------- ------ - -- -------- ------ ------ - -
上述代码中,我们在 reducers 文件夹下定义了一个 todosReducer 函数,该函数接收两个参数:state 和 action,其中 state 是当前的 state(必须是 JavaScript 对象),action 是一个带有 type 属性的普通 JavaScript 对象,在这里,使用了 ES6 的默认参数语法,对 state 进行了默认值的处理,即初始化为空数组。
在该例子中,我们采用了 switch case 控制流,根据不同的 action 类型,返回不同的 state。当接收到 ADD_TODO 的 action 时,则会将新的 todo 对象追加到 state.todos 数组的末尾。
2、Redux 内部是如何执行 reducer 的呢?
在 Redux 内部,怎么样执行 reducer 就变得尤为重要了。下面,我们将以 Redux 中 store.subscribe() 方法的执行流程为例,介绍 reducer 的执行机制。
下面是 Redux 源码中触发 reducer 的代码片段(其中 stateChanger(reducer) 方法返回一个实际的 reducer 函数),结合注释理解,如下:
-- ------- ------- --- ----------- - -------------- - -------- ------------ - ---------------------------- ---- - ----- -------------- -------- ------- ------- ----- ----- ------ --- ------- ------- ----- --- --- --------- - --- -- --------- ----------- --- ---------- ------------ ------ - --------- ---------- --------- -- -- --------- ---- ------- --- ----- -------- --------------------- - ------ --------------- ------- - ----- -------- - -------------- -------- ----- - --------- ------ --------- -- - -- --------- --- ----- -------- -------- ------------------- - ------------------------- -- --------- --- ------ ---------- - --------- - ------------------ -- - --- ---------- -- - -- ------- -- ------ -- ------------ ----- -------- ---------------- - ------------ - ---------------------------- -------- ------------------- -- ----- -
在 Redux 源码中,store.subscribe(listener) 方法接收一个 listener 函数,用于监测 state 的变化。在新的 action 被 dispatch() 到 store 时,store 会先使用最新的 reducer 来计算出新的 state,然后遍历所有的 listeners,依次执行它们。
如上代码片段所示,Redux 内部定义一个 stateChanger(reducer) 函数,对通过 dispatch() 方法传递的 action 进行处理。该函数内先对 reducer 进行判断,如果 reducer 是函数,则将当前 state 和空 action,通过 reducer 的处理器函数,进行初始化状态的创建。
在发布消息时,将计算出的新的 state 存入 currentState 中,并逐个更新之前添加的 listeners 函数。每个 listener 函数都通过 store.getState() 方法获取最新的 state 数据,并进行相应的渲染操作。
3、结合实例理解 reducer 的状态变化和应用
了解了 Redux 状态管理的基本原则和 reducer 的实现机制之后,下面我们使用一个实例来更详细地了解 reducer 的神奇变换机制。
此处,我们假设一个顾客加入一个购物车后,购物车中商品的状态如何变化。这里以 React 的 Redux模块下的 combineReducers() 方法为例,该方法提供了用于定义 reducer 的优雅方法。代码如下:
------ - --------------- - ---- -------- ------ ----------- ---- ---------------- ------ --------------- ---- -------------------- -- -- ---------- ---------------- ----- ----------- - ----------------- ----- ------------ --------- ---------------- --- ------ ------- ------------
combineReducers() 方法接收一个对象作为参数,该对象以 reducer 名称作为键,以实际 reducer 函数作为值,将多个 reducer 组合成一个 reducer 函数(称之为 rootReducer)。我们来看看 cartReducer.js 文件下的代码,购物车数据的初始化和更新均通过该文件实现:
------ - ------------ ---------------- - ---- ----------------- ----- ------------ - - ------ --- ------ -- -- -- ----------- --------------- - ------------- -------- ----------------- - ------------- ------ - --- - ----- - ----- ------- - - ------- ------ ------ - ---- ------------ ------ - --------- ------ ---------------- -------------- ------ ----------- - ------------------- -- ---- ----------------- ------ - --------- ------ ----------------------- -- ------- --- ----------------- ------ ----------- - ------------------- -- -------- ------ ------ - - ------ ------- ------------
在上述代码片段中,我们定义了一个 cartReducer,该 reducer 接收两个参数,分别是 state 和 曾在 action 中通过 dispatch() 方法传递过来的一个 type 及空对象。
在新商品添加到购物车中时,我们需要修改 state 中的两个状态:items 是一个数组,代表购物车中所有商品的信息,price 是一个总价格字段。在 ADD_TO_CART 类型的 action 中,我们使用展开的对象语法,将初始 state 的数组 items 中添加商品对象,并增加总价格。在 REMOVE_FROM_CART 类型的 action 中,我们从 items 数组中移除商品对象,并减小总价格。
那么,我们如何在 React 中使用它呢?假设我们再定义一个组件 ShoppingCart,来渲染购物车内的商品信息:
------ ----- ---- -------- ------ - ------- - ---- -------------- ----- ------------ ------- --------------- - -------- - ----- - ------ ----- - - ----------- -- --------- ----- ------------- - -------------- -- --- -------------------------------------------- -- -- ------- ----- ------------- - ------------------ ------ - ----- -------------- ------------------------ --------------- ------ - - - -------- ---------------------- - ------ - ------ ----------------- ------ ----------------- -- - ------ ------- ---------------------------------------
通过使用 redux 的 connect() 函数,将 ShoppingCart 组件和购物车的 state 进行关联,将其中的 items 和 price 属性在组件中从 this.props 中拿到,并渲染到页面上。
4、总结
通过以上实例,我们得以更深入地认识 Redux 中 reducer 的状态变化和变换机制,理解了 reducer 如何与 store、action、state 三者协作来实现状态管理。在使用 Redux 进行前端开发时,更多地了解 reducer 的实现原理,可以为开发者避免一些常见的错误,并加深对全局状态更新过程的运作机制的认识,从而更好地使用 Redux 进行数据管理。
来源:JavaScript中文网 ,转载请联系管理员! 本文地址:https://www.javascriptcn.com/post/64ca05bc5ad90b6d04190dc3