npm 包 redux-functional-reducer 使用教程

阅读时长 22 分钟读完

React 和 Redux 一直是前端开发中最流行和最强大的框架之一。React 用于构建 User Interface,而 Redux 则用于管理应用程序的状态。Redux 提供了一个简单且可预测的数据流方案,使得在复杂应用中管理状态变得方便。然而,Redux 的 reducer 处理方式需要程序员熟练使用语言特性和设计模式。为了解决这个问题,出现了 redux-functional-reducer 这个包。

redux-functional-reducer 是什么?

简单来说,redux-functional-reducer 是一个可视化工具,它可以帮助我们更好地管理应用程序的状态。它使用函数式编程风格处理状态管理,不再是传统的 switch 和 if/else 控制流编写,更符合现代 JS 语言特性和风格。

redux-functional-reducer 提供了四个高阶 reducer,分别是 composeReducers, mapReducers, whenRejectedwhenUnhandled,我们会在下面的示例中详细了解它们。

安装

安装 redux-functional-reducer 很简单,使用 npm 安装:

使用

为了更好地说明如何使用 redux-functional-reducer,我们将创建一个具有如下功能的简单应用:

  • 一个 todo 列表
  • 可以添加、删除和完成 todo

我们将从 Redux 基础模板开始,并使用 redux-functional-reducer 优化我们的 reducer。

基础模板

我们从最基础的 Redux 模板开始,它包含了一个简单的 todo 的 actions 和 reducer:

-- -------------------- ---- -------
----- -------- - -----------
----- ----------- - --------------

----- ------------ - -
  ------ --
--

-------- ------------------ - ------------------- ------- -
  ------ ------------- -
    ---- ---------
      ------ -
        ---------
        -
          --- ------------ - --
          ----- ------------
          ---------- -----
        -
      --
    ---- ------------
      ------ -------------- --
        ------- --- --------- - - -------- ---------- --------------- - - ----
      --
    --------
      ------ ------
  -
-

------ ------- -------------

这个模板很简单,只有两个 actions 和一个 reducer,但是在复杂的应用程序中,reducer 通常是一大块 switch 和 if/else 语句。因此,我们使用 redux-functional-reducer 来优化这个 reducer。

使用 redux-functional-reducer

接下来,我们将使用 redux-functional-reducer 来编写我们的 reducer。

首先,由于我们的应用程序中有多个 reducers,我们需要使用 combineReducers 将它们组合起来,生成一个根 reducer。然后,我们使用 composeReducerstodosReducer 编写成函数式 reducer。

-- -------------------- ---- -------
------ - --------------- - ---- --------
------ - --------------- - ---- ---------------------------

----- -------- - -----------
----- ----------- - --------------

----- ------------ - -
  ------ --
--

-------- ------------------ - ------------------- ------- -
  ------ ------------- -
    ---- ---------
      ------ -
        ---------
        -
          --- ------------ - --
          ----- ------------
          ---------- -----
        -
      --
    ---- ------------
      ------ -------------- --
        ------- --- --------- - - -------- ---------- --------------- - - ----
      --
    --------
      ------ ------
  -
-

----- ----------- - -----------------
  ------ ----------------
    -------------
    -------------------- -- ----------------------- ------- -----------------
  -
---

我们组合了 todosReducer 和 whenUnhandled。whenUnhandled 用于捕捉 todo actions 以外的 actions,防止我们的 reducer 抛出错误。

mapReducers 控制多个 reducer

现在我们想增加一个新的 reducer,用于控制 todo 的 visibilityFilter(过滤器)。

在 rootReducer 中,我们需要为两个 reducer 分别提供 action。

很显然,我们需要手动的将 action 传递给每个 reducer。此时,我们需要 mapReducers

-- -------------------- ---- -------
------ - --------------- - ---- --------
------ - ---------------- ------------ ------------- - ---- ---------------------------

----- -------- - -----------
----- ----------- - --------------
----- --------------------- - ------------------------

----- ------------ - -
  ----------------- -----------
  ------ --
--

-------- ------------------ - ------------------- ------- -
  ------ ------------- -
    ---- ---------
      ------ -
        ---------
        -
          --- ------------ - --
          ----- ------------
          ---------- -----
        -
      --
    ---- ------------
      ------ -------------- --
        ------- --- --------- - - -------- ---------- --------------- - - ----
      --
    --------
      ------ ------
  -
-

----- ----------------------- - ------ - ------------------------------ ------- -- -
  -- ------------ --- ---------------------- -
    ------ --------------
  -
  ------ ------
--

----- ----------- - ----------------
  -------------
    ----------------- ------------------------
    ------ ------------
  ---
  -------------------- -- ----------------------- ------ ----- -----------------
--

------ ------- ------------

现在,我们使用了 mapReducerscomposeReducers,可以将 reducers 组合在一起,并处理所有的 action。此时,我们的应用程序状态如下:

同时,我们还通过 whenUnhandled 避免了未知 action 的错误。

whenRejected 控制异步错误

在复杂的应用程序中,API 调用和异步操作是必需的。因此,我们需要处理异步错误和在 UI 中通知错误。

当我们使用 redux-thunk 或 redux-saga 等 middleware 时,抛出 error 对 reducer 不安全,redux 异步错误处理通常使用 actions 和 reducers 的结合。因此,redux-functional-reducer 提供了 whenRejected,它是一个拦截器,负责处理 action 的 error 信息。

我们使用 React 的服务端渲染(SSR)流程来展示 whenRejected。通常,我们需要在 UI 中通知用户错误信息。

下面是一个模拟异步 API 接口,模拟返回一篇不存在的文章:

我们的 onError 函数可以根据返回的 error,展示错误 UI。

-- -------------------- ---- -------
------ ----- ---- --------
------ - -------- - ---- --------------
------ - ---------------- ----------- - ---- --------
------ ----- ---- --------------
------ ----------- ---- -------------
------ - ---------------- ------------ -------------- ------------ - ---- ---------------------------

----- -------- - -----------
----- ----------- - --------------
----- --------------------- - ------------------------

----- ------------ - -
  ----------------- -----------
  ------ --
--

-------- ------------------ - ------------------- ------- -
  ------ ------------- -
    ---- ---------
      ------ -
        ---------
        -
          --- ------------ - --
          ----- ------------
          ---------- -----
        -
      --
    ---- ------------
      ------ -------------- --
        ------- --- --------- - - -------- ---------- --------------- - - ----
      --
    --------
      ------ ------
  -
-

----- ----------------------- - ------ - ------------------------------ ------- -- -
  -- ------------ --- ---------------------- -
    ------ --------------
  -
  ------ ------
--

----- ----------- - ----------------
  -------------
    ----------------- ------------------------
    ------ ------------
  ---
  ------------------ --------- --------- -- -
    ------------------ ------- --------- -----
    ----- ----- - ---- ------- ------- --------------- ----- ------------------
    ------ -
      ----- ------------
      -------- -----
    --
  ---
  -------------------- -- ----------------------- ------ ----- -----------------
--

------ ------- ------------

我们将 whenRejectedmapReducerscomposeReducers 中的其他 reducers 结合使用,以便能够处理 actions 和捕捉未处理的 actions。

您可能已经注意到,此时我们返回一个 action { type: 'API_ERROR', payload: '...' },稍后可以在我们的组件中处理它。

-- -------------------- ---- -------
-------- ----- --------- -------- -- -
  ----- ------- --------- - -------------------
  ----- ------------ - --- -- -
    -------------------
    -------------------------
    -------------
  --

  ----- ------------------ - -- -- -
    ------ -------------------------
  --

  ------ -
    ---- ----------------
      --------------
      ----- ------------------------
        -------
          --- -----
          ------ ------------- ------------ ----------- -- ------------------------- --
        --------
        ------- -----------------------------
      -------
      ------- ---------------------------------- ----------------
      --------- -- --- -------- ------ ----- -------------------
    ------
  --
-

如果我们在组件中调用不存文章的 id,则出现这个编码错误。

-- -------------------- ---- -------
------ - ---------------- - ---- --------
------ - ------------ - ---- ------------------------

----- ------------ - ---------------

------ ----- ----------- - ---- -- ----- ---------- --------- -- -
  --- -
    ----- -------- - ----- -----------------------------------
    ---------- ----- ------------- -------- -------- ---
  - ----- ------- -
    ----- - -------- ---------- - - ------
    ------------------------ ---------------- - -------- ---------- ---
    ----- ------
  -
--

发生错误后,我们在 UI 中看到了错误信息:

总结

在本文中,我们介绍了 redux-functional-reducer,并展示了如何使用它来帮助我们更好地管理 Redux 应用程序的状态。redux-functional-reducer 的 composeReducers, mapReducers, whenRejectedwhenUnhandled 提供了很多好处,帮助我们更好地组织和控制应用程序。通过使用 redux-functional-reducer,我们可以构建更健壮、更可扩展和更可维护的应用程序。

下面是完整的代码示例:

-- -------------------- ---- -------
-- -----------
------ - --------------- - ---- --------
------ - ---------------- ------------ -------------- ------------ - ---- ---------------------------
------ - ---------------- - ---- --------
------ - ------------ - ---- ------------------------

----- -------- - -----------
----- ----------- - --------------
----- --------------------- - ------------------------
----- ------------ - ---------------

----- ------------ - -
  ----------------- -----------
  ------ ---
  --------- ----
--

-------- ------------------ - ------------------- ------- -
  ------ ------------- -
    ---- ---------
      ------ -
        ---------
        -
          --- ------------ - --
          ----- ------------
          ---------- -----
        -
      --
    ---- ------------
      ------ -------------- --
        ------- --- --------- - - -------- ---------- --------------- - - ----
      --
    --------
      ------ ------
  -
-

----- ----------------------- - ------ - ------------------------------ ------- -- -
  -- ------------ --- ---------------------- -
    ------ --------------
  -
  ------ ------
--

----- --------------- - ------ - ---------------------- ------- -- -
  -- ------------ --- ------------ -
    ------ ---------------
  -
  ------ ------
--

----- ----------- - ----------------
  -------------
    ----------------- ------------------------
    ------ -------------
    --------- ---------------
  ---
  ------------------ -- -
    ------------------ ------- --------- -----
    ----- ----- - ---- ------- ------- ----------------
    ------ -
      ----- ------------
      -------- -----
    --
  ---
  -------------------- -- ----------------------- ------- -----------------
--

------ ------- ------------

-- ------
------ ----- ---------------- - -- -- --- ----------------- ------- -- -
  ------------- -- -
    ------ -------- --- -------- -------- --- ------ ---
  -- -----
---

-- ----------
------ ----- ------- - ------ -- -
  ------ -
    ----- ---------
    ----
  --
--

------ ----- ---------- - ---- -- -
  ------ -
    ----- ------------
    --
  --
--

------ ----- ------------------- - -------- -- -
  ------ -
    ----- ----------------------
    ------
  --
--

------ ----- ----------- - ---- -- ----- ---------- --------- -- -
  --- -
    ----- -------- - ----- -----------------------------------
    ---------- ----- ------------- -------- -------- ---
  - ----- ------- -
    ----- - ------- - - ------
    ------------------------ ---------------- - ------- ---
    ----- ------
  -
--

-- ------
------ ----- ---- --------
------ - -------- - ---- --------------
------ - ---------------- ----------- - ---- --------
------ ----- ---- --------------
------ ----------- ---- -------------
------ - ---------------- ------------ -------------- ------------ - ---- ---------------------------
------ - -------- ----------- -------------------- ----------- - ---- ------------
------ - ------------------ ------------ - ---- ------------------------

-------- ----- -------- -- -
  ----- - ----------------- - - --------------------
  ----- ------- --------- - -------------------

  ----- ------------ - --- -- -
    -------------------
    -------------------------
    -------------
  --

  ----- ------------------ - -- -- -
    ------ -------------------------
  --

  ------ -
    ---- ----------------
      --------------
      ----- ------------------------
        -------
          --- -----
          ------ ------------- ------------ ----------- -- ------------------------- --
        --------
        ------- -----------------------------
      -------
      ------- ---------------------------------- ----------------
      ------------------ -- --------------------
      --------- -- --- -------- ------ ----- -------------------
    ------
  --
-

----- ---------- - --------

----- ----- - ------------
  ------------
  ------------------------------
--

-------- ------ -
  ------ -
    --------- --------------
      ---- ------------------------- --
    -----------
  --
-

------ ------- -----

来源:JavaScript中文网 ,转载请注明来源 https://www.javascriptcn.com/post/60055ea381e8991b448dbfe8

纠错
反馈