React Hook 深入解析:useReducer 的应用场景

阅读时长 11 分钟读完

React Hook 是 React 16.8 版本中引入的新特性,它可以让我们在函数组件中使用 React 的 state、生命周期等功能。在所有 Hook 中,useReducer 是最强大的一个,它为我们提供了一种更优雅、更可预测以及更易于测试的方式来管理复杂的状态逻辑。本文将会深入探讨 useReducer 在 React 中的应用场景以及如何实现一个自定义 Hook。

useReducer 简介

在探讨 useReducer 的应用场景之前,我们需要先了解一下它是如何工作的。useReducer 的签名如下:

  • state:表示状态的值
  • dispatch:一个函数,用来将 action 分发给 reducer 函数
  • reducer:一个函数,接收当前状态和分发的 action,返回新的状态值
  • initialArg:初始化状态的值
  • init:一个可选的函数,可以将复杂的状态进行初始化

当我们使用 useReducer 时,相当于我们创建了一个可控制的状态容器,状态容器中存储的数据是由我们自己定义的 reducer 函数决定的。一个简单的例子是:

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

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

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

在上面的例子中,我们创建了一个初始状态为 {count: 0} 的状态容器,并创建了一个 reducer 函数。在组件中,我们调用了 useReducer 函数,将容器和 reducer 组合在了一起,同时创建了两个按钮,分别在我们点击时向容器中分发了 incrementdecrement 的 action。每次分发 action 时,容器中的状态都会按照我们在 reducer 函数中定义的逻辑进行更新,并在 React 中重新渲染视图,从而实现了计数器的增减功能。

在这个例子中,我们看到了 useReducer 函数的核心作用,它创建了一个容器来存储我们的状态,并通过 dispatch 函数向容器中分发 action,最终由 reducer 函数来更新容器中的状态值。

useReducer 的应用场景

上面的例子展示了 useReducer 的最基本的用法,但实际上 useReducer 还有很多应用场景。接下来,我们将探讨一些常见的场景。

状态过于复杂

当我们的状态过于复杂、嵌套层次过深时,我们可以选择用 useReducer 来管理状态。一个例子是一个日历应用,我们需要展示不同的事件数据、一个当前的视图模式以及选中的日期。

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

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

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

在上面的例子中,我们可以看到这个日历应用的状态相当复杂,有多个字段、嵌套层次较深。我们使用 useReducer 可以很方便地管理这种复杂的状态变化,同时也使得我们的代码更加易于维护和测试。

表示异步操作的状态

有些状态既表示同步数据的状态,又表示异步操作的状态。例如,在一个带有搜索功能的应用中,我们可能需要表示两种状态:请求状态和搜索结果状态。

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

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

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

在上面的例子中,我们使用 useReducer 来表示搜索的状态,在请求开始时状态变为 loading,在请求成功时状态变为 idle,并且将搜索结果保存在 data 中。如果请求失败,则将状态也设置为 idle,同时清空搜索结果。

在上面的例子中,我们使用 useReducer 来管理状态,而不是仅仅使用 useState,这是因为 state 变量需要同时表示同步和异步数据,而 useReducer 可以更好地处理状态的复杂性。

可撤回和重做的操作

假设我们有一个可撤回和重做的编辑器应用,我们可能需要记录每一个操作的历史记录,从而能够撤销和重做操作。

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

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

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

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

在上面的例子中,我们使用 useReducer 来管理整个应用的状态,包括过去、当前、和未来的状态。当我们分发 UPDATE_DOCUMENT 的 action 时,将当前状态添加到过去状态中,然后将新的状态设置为当前状态。当我们分发 UNDO 的 action 时,将当前状态添加到未来状态中,然后将过去的状态设置为新的当前状态。REDO 操作类似。

实现一个自定义 Hook

使用 useReducer 比使用 useState 更为强大。实际上,大多数使用 useState 的场景都可以使用 useReducer 来实现。我们可以将复杂的状态转换逻辑分解到 reducer 函数中,从而将组件本身变得更为简单。

在本节中,我们将实现一个自定义 Hook,它将使用 useReducer 来管理一个异步请求的状态。在实现之前,我们需要了解一下 useState 和 useReducer 之间的区别。useState 会在每次组件渲染之后重新创建一个新的状态变量。而 useReducer 则在每次渲染时使用相同的状态变量,只是根据传递给它的 action 进行了更新。

我们的自定义 Hook 将使用 useReducer 来存储一些关于请求的状态信息:

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

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

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

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

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

在上面的例子中,我们定义了一个 useAsyncRequest(命名不规范)自定义 Hook,它接收一个 URL 作为输入。使用 useReducer 和几个 action,我们管理了一个包含了请求状态、响应数据和错误信息的状态对象。当我们向 fetchData 函数分发 REQUEST_START 的 action 时,将状态设置为 FETCHING,数据和错误信息都设置为 null。当请求成功时,我们将数据添加到状态中,同时将状态设置为 SUCCESS。当请求失败时,我们将错误信息添加到状态中,同时将状态设置为 FAILURE

使用自定义 Hook 的代码如下:

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

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

在上面的例子中,我们使用自定义 Hook useAsyncRequest 从后端获取数据,并根据响应状态展示不同的状态信息。

结论

useReducer 可以帮助我们管理复杂的状态,同时也能使我们的代码更加可维护。通过将状态容器分解为独立的 reducer 函数,我们可以将状态的逻辑分解到不同的函数中,从而使代码变得更加模块化和可读性更高。在我们的应用中,可以通过 useReducer 来表示复杂的状态结构,管理异步数据的状态、记录撤消和重做操作等。

总之,使用 useReducer 和自定义 Hook 可以使我们的 React 应用更加强大和易于维护。

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

纠错
反馈