React 状态管理:使用 Context 和 Reducer

React 是一个流行的前端开发框架,它拥有强大的、可重用的组件模型。然而,在你的应用程序中管理组件状态时,你可能会遇到令人沮丧的问题:传递状态和回调函数的深层嵌套层次结构。

为了解决这些问题,React 提供了一些机制使得状态管理和组件通信变得更加简单。在本文中,我们将讨论其中的两个机制:Context 和 Reducer。

Context

Context 是一种在组件之间共享数据的方式,它允许你通过组件树传递数据而不必手动地一级一级地将它们传递下去。这对于某些类型的属性(例如语言环境或主题)非常有用,并且它使得在组件中跨越多层级传递数据变得更加容易。

创建 Context

首先,让我们看看如何创建我们自己的 Context。我们不需要安装任何依赖项 - React 已经在其核心库中提供了它。

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

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

我们可以通过向 createContext 方法传递一个默认值来创建 Context,这个默认值只有在没有 Provider 时才会生效。

提供值

接下来,我们需要创建一个上下文提供者 (Provider),并将值传递给它:

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

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

在上面的例子中,我们创建了一个 MyProvider 组件,在它的 render 方法中渲染 MyContext.Provider,并将 value 属性设置为 this.state.data。这个 value 将会被新的 Consumer 组件使用。

使用 Consumer

现在,我们可以在任何组件中使用 MyContext 中的数据了,只要它们被 MyProvider 的子组件所包裹。要使用 Context 数据,我们需要使用另一个 React 组件 Consumer

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

在这个例子中,我们创建了一个 MyConsumer 组件,并使用 Consumer 组件,来读取传递给上下文提供者的值。

示例应用

在这个示例应用中,我们将创建一个简单的事件计数器,这个计数器能够从任何组件中调用,并从 store 中获取数据。

首先,我们创建一个名为 Store 的文件,它将包含我们的数据以及操作数据的函数。

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

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

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

在这个例子中,我们创建了两个常量。第一个常量 Store 是我们的上下文,第二个常量 initialState 定义了我们 store 的默认状态,第三个常量 reducer 是用来更新 store 的纯函数。在这个简单的例子中,我们只有一个 increment 命令来增加计数器的数值。

接下来,我们将会创建一个名为 App 的组件,在这个组件中,我们将渲染给定的 children,并且提供一个 Store 上下文。我们还将定义 Store 的默认值,以防我们没有向 StoreProvider 传递值。

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

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

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

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

在这个例子中,我们创建了一个 App,它的 state 属性包含了整个 storedispatch 函数。在 render 函数中,我们通过将 storedispatch 传递给 Store,来提供 Store 上下文。

现在,我们可以在 App 中使用任何我们希望共享数据的组件。

比如 CounterButton 组件,这个组件渲染一个简单的 button,在被点击时调用 dispatch 函数:

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

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

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

在这个例子中,我们使用 Consumer 组件来读取 store 中的值和 dispatch 函数,并使用 onClick 事件调用 dispatch

现在我们已经为我们的应用程序对象提供了上下文提供者和使用上下文提供者的组件,我们可以在所有需要使用数据的组件中使用他们了。

Reducer

在前面的示例中,我们通过在 dispatch 函数中使用 reducer 列表来更新状态。在这个简单的例子中,这是非常直观的。但是在更复杂的情况下,可能会需要进行更复杂的状态更改。在这种情况下,使用 React 的 Reducer 可以解决这个问题。

Action 类型

我们已经在之前的示例中使用了 action 对象来更改状态。在 Reducer 中,action 对象包含了一个 type 属性,它描述了我们应该执行的操作类型,以及可选的其他属性。

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

在这个例子中,我们定义了两个不同的 actionincrementActiondecrementAction

Reducer 函数

Reducer 是一个纯函数,它接受当前状态和一个 action 对象并返回一个新的状态。

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

在这个例子中,我们定义了一个 reducer 函数,它捕获类型为 'increment''decrement'action。在 "increment" 操作中,我们是用展开操作符 (...) 复制了当前状态,通过 count 属性增加数值。在 "decrement" 操作中使用了进一步一些操作,也通过 count 属性去减去 payload 属性作为减数的数值。

使用 Reducer

前面几节介绍了 Reducerdispatch 和我们上下文的设置。将它们结合在一起,按照 React 的官方文档中的建议来完成 Reducer 的使用。

我们使用 useReducer 来传入一个“Reducer 函数”,一个“初始状态”,以及一个“中间状态”的优化函数。然后就会返回一个 state 和 dispatch:

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

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

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

在这个代码示例中,我们使用了 useReducer api 初始化了 statedispatch。又用 value 属性来传入 context。

我们可以像前面一样,即在下游的 Consumer 中使用该 Context:

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

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

在本例子中,我们使用了 useContext 来获得 context 的值。一旦 statedispatch 发生更改,组件都将会重新渲染。我想提醒一下小伙们,千万不要忘了使用判断或者 useMemo 做性能优化。

当然,这仅仅只是 Reducer 的一个用例,它也可以用于处理更加复杂的状态更改逻辑。

结论

在本文中,我们介绍了使用 React 的 Context 和 Reducer 来管理状态的方式。我们创建了上下文提供者来共享数据,创建了使用上下文数据的消费者。在这些例子中,我们使用了 Reducers 作为一个状态管理器,注重解决了在更新多个操作的情况下,使用 Redux 和 Context 这样的状态管理库所需要的一些代码。

我希望此文章对您理解 React 的 Context 和 Reducer 有所帮助,它是一个更快,更干净,更简单的状态管理器。

来源:JavaScript中文网 ,转载请注明来源 本文地址:https://www.javascriptcn.com/post/670c724666ef9cf37fb13fcb