从 redux 源码中探究 reducer 如何 “神奇的” 变换 state

从 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


猜你喜欢

  • ES7 async/await 功能的详细介绍及实际使用案例

    异步编程是 JavaScript 中一个非常重要的概念,而在 ES7 中,添加了 async/await 这一新特性,以更加简单和可控的方式来处理异步操作。本文将详细介绍 async/await 的语...

    1 年前
  • MongoDB 中的全文检索优化方法

    在大数据时代,数据量的增加给全文检索带来了新的挑战。MongoDB 作为一个颇为流行的数据库,在提供完整的文本检索功能的同时,也面临着检索效率低下的问题。本文将介绍 MongoDB 中的全文检索优化方...

    1 年前
  • SASS 中 @at-root 与规则插入

    在前端开发中,使用 CSS 预处理器可以提高开发效率和代码可维护性。SASS 作为其中的一种领先者,已经成为了众多开发者的首选。本文将介绍 SASS 的一个重要特性 @at-root 和规则插入功能,...

    1 年前
  • 实现 CSS Grid 布局的最佳实践

    CSS Grid 布局是一种用于设计响应式网格的强大工具。可以用于设计方便易用的布局,从而使网站看起来更加专业和有条理性。本文将为您介绍如何实现最佳实践的 CSS Grid 布局。

    1 年前
  • webpack配置中的 devServer 详解

    在前端开发中,Webpack 是一个很常用的工具。它可以将多个 JS 文件打包合并为一个文件,也可以转换 JSX 和 ES6 代码为浏览器可以识别的代码。除此之外,Webpack 还有一个非常重要的功...

    1 年前
  • TypeScript 中的装饰器使用教程

    装饰器是 TypeScript 中的一种特殊语法,它可以在类和类的属性和方法上添加一些特殊的元数据。TypeScript 的装饰器是以 @ 符号为前缀的一个函数,它可以被附加到类的声明、方法、属性或参...

    1 年前
  • 解决在 Node.js 中使用 ES11 的 import/export 模块语法的问题

    在 ES6 中引入了 import/export 语法,让 JavaScript 模块化开发变得更加方便和标准化。然而,这些语法在 Node.js 中使用时会出现一些问题,例如无法直接使用 impor...

    1 年前
  • 错误汇总:如何处理 ESLint 带来的语法提示

    错误汇总:如何处理 ESLint 带来的语法提示 作为一名前端开发人员,我们都知道 ESLint 是一个非常有用的工具,可以在编码的过程中为我们提供语法错误、拼写错误等提示,能够大大减少我们代码中的错...

    1 年前
  • 使用 RxJS 实现 Redux middleware

    使用 RxJS 实现 Redux middleware 在前端开发中,Redux 是一种流行的数据管理库,它可以帮助我们管理应用程序的状态数据。同时,Redux 提供了 middleware 的功能,...

    1 年前
  • 解决无障碍设备中视频播放等模块存在的问题

    背景 在现代化的移动互联网时代,视频播放已经成为了重要的信息传播方式,然而,我们的视觉系统只是人类感知能力的一个方面。全球有超过1.3亿人处于不同种类的失聪或失明状态,这些人在使用电脑或移动设备时也需...

    1 年前
  • Chai 如何断言一个函数是否返回了一个 Promise

    背景介绍 前端测试是保证软件质量的关键步骤之一。随着前端技术的发展,前端测试也越来越重要。而断言是测试中的核心步骤,它是用来确认一个结果是否符合预期。在前端测试中,使用 Chai 断言库可以方便地进行...

    1 年前
  • Redux 源码阅读笔记——applyMiddleware

    Redux 是一种强有力的状态管理器,可以为中大型应用程序提供可预测的状态管理机制。Redux 的核心代码非常小巧,也非常明晰,但是其实现过程可以比较复杂。 其中,Redux 的 applyMiddl...

    1 年前
  • Android Material Design 中实现多种 Header 样式

    随着 Android Material Design 的不断发展,Header 样式在应用中扮演了非常重要的角色,不仅仅是提供应用标题的作用,而且还有着丰富、多样化的样式,让应用更加美观、用户友好。

    1 年前
  • Redis 架构设计与实现方法的详解

    简介 Redis 是一个内存数据库,拥有极高的性能和可靠性,在前端领域中应用广泛。Redis 将所有数据保存在内存中,读写速度非常快,同时支持多种数据结构,配合良好的持久化方案,使得 Redis 在数...

    1 年前
  • 如何用 PWA 技术实现可离线的电子商务网站?

    前言 近年来,随着移动端设备的普及,手机成为了人们生活中不可或缺的工具之一。同时,人们对于网站性能和用户体验的要求也越来越高。因此,一种名为“渐进式Web应用程序(PWA)”的技术逐渐兴起。

    1 年前
  • 解决 IE 浏览器不支持 Custom Elements 的问题

    前言 Custom Elements 是 Web Components 中的一种实现方式,可以让开发者自定义 HTML 元素,扩展出更丰富的标签来使用。然而,IE 浏览器并不支持 Custom Ele...

    1 年前
  • 如何在 Koa 框架中使用 Mongoose 进行 MongoDB 操作

    本文将介绍如何在 Koa 框架中使用 Mongoose 进行 MongoDB 操作。Mongoose 是一个优秀的 Node.js ORM 框架,能为开发者提供方便的 API 来操纵 MongoDB ...

    1 年前
  • ES10 的 Unicode 正则表达式匹配详解及最佳实践

    在 JavaScript 的开发中,正则表达式是十分常见的一种工具,它可以帮助我们在字符串中匹配指定的模式。而随着 ES10(ECMAScript 2019)的发布,通过 Unicode 相关的功能,...

    1 年前
  • Next.js 如何处理多语言路由

    Next.js 如何处理多语言路由 随着全球互联网的迅猛发展,多语言网站已成为企业营销的一个重要手段。对于前端开发人员来说,实现多语言路由是一个不可避免的问题。在 Next.js 中,处理多语言路由非...

    1 年前
  • 用 Tailwind 实现简洁无比的卡片式布局

    在现代 web 开发中,卡片式布局越来越受欢迎。这种布局可以将页面中的元素分离出来,使其更具可读性和可视性。如果你正在寻找一种简单、实用且易于定制的方法来实现卡片式布局,那么 Tailwind CSS...

    1 年前

相关推荐

    暂无文章