React 是一个流行的前端开发框架,它拥有强大的、可重用的组件模型。然而,在你的应用程序中管理组件状态时,你可能会遇到令人沮丧的问题:传递状态和回调函数的深层嵌套层次结构。
为了解决这些问题,React 提供了一些机制使得状态管理和组件通信变得更加简单。在本文中,我们将讨论其中的两个机制:Context 和 Reducer。
Context
Context 是一种在组件之间共享数据的方式,它允许你通过组件树传递数据而不必手动地一级一级地将它们传递下去。这对于某些类型的属性(例如语言环境或主题)非常有用,并且它使得在组件中跨越多层级传递数据变得更加容易。
创建 Context
首先,让我们看看如何创建我们自己的 Context。我们不需要安装任何依赖项 - React 已经在其核心库中提供了它。
import React from 'react'; const MyContext = React.createContext('default_value');
我们可以通过向 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
属性包含了整个 store
和 dispatch
函数。在 render
函数中,我们通过将 store
和 dispatch
传递给 Store
,来提供 Store
上下文。
现在,我们可以在 App
中使用任何我们希望共享数据的组件。
比如 CounterButton
组件,这个组件渲染一个简单的 button
,在被点击时调用 dispatch
函数:
-- -------------------- ---- ------- ------ ----- ---- -------- ------ - ----- - ---- ----------- ----- ------------- ------- --------------- - -------- - ------ - ---------------- --- ------ -------- -- -- - ------- ----------- -- ---------- ----- ----------- ---- ------- ------------- ----- --------- -- ----------------- -- - - ------ ------- --------------
在这个例子中,我们使用 Consumer
组件来读取 store 中的值和 dispatch
函数,并使用 onClick
事件调用 dispatch
。
现在我们已经为我们的应用程序对象提供了上下文提供者和使用上下文提供者的组件,我们可以在所有需要使用数据的组件中使用他们了。
Reducer
在前面的示例中,我们通过在 dispatch
函数中使用 reducer
列表来更新状态。在这个简单的例子中,这是非常直观的。但是在更复杂的情况下,可能会需要进行更复杂的状态更改。在这种情况下,使用 React 的 Reducer 可以解决这个问题。
Action 类型
我们已经在之前的示例中使用了 action
对象来更改状态。在 Reducer 中,action
对象包含了一个 type
属性,它描述了我们应该执行的操作类型,以及可选的其他属性。
const incrementAction = { type: 'increment' }; const decrementAction = { type: 'decrement', payload: 10 };
在这个例子中,我们定义了两个不同的 action
,incrementAction
和 decrementAction
。
Reducer 函数
Reducer 是一个纯函数,它接受当前状态和一个 action
对象并返回一个新的状态。
-- -------------------- ---- ------- ----- ------- - ------- ------- -- - ------ ------------- - ---- ------------ ------ - --------- ------ ----------- - - -- ---- ------------ ------ - --------- ------ ----------- - -------------- -- -------- ------ ------ - --
在这个例子中,我们定义了一个 reducer
函数,它捕获类型为 'increment'
和 'decrement'
的 action
。在 "increment"
操作中,我们是用展开操作符 (...
) 复制了当前状态,通过 count
属性增加数值。在 "decrement"
操作中使用了进一步一些操作,也通过 count
属性去减去 payload
属性作为减数的数值。
使用 Reducer
前面几节介绍了 Reducer
,dispatch
和我们上下文的设置。将它们结合在一起,按照 React 的官方文档中的建议来完成 Reducer 的使用。
我们使用 useReducer
来传入一个“Reducer 函数”,一个“初始状态”,以及一个“中间状态”的优化函数。然后就会返回一个 state 和 dispatch:
-- -------------------- ---- ------- ------ ------ - ----------- ------------- - ---- -------- ------ - ------------- ------- - ---- ------------ ------ ----- ----- - ---------------------------- ------ ----- ------------- - -- -------- -- -- - ----- ------- --------- - ------------------- -------------- ------ --------------- -------- ------ -------- ------------------------------- --
在这个代码示例中,我们使用了 useReducer
api 初始化了 state
和 dispatch
。又用 value
属性来传入 context。
我们可以像前面一样,即在下游的 Consumer 中使用该 Context:
import React from 'react'; import { Store } from './store'; export default function CounterButton() { const { state, dispatch } = React.useContext(Store); const { count } = state; return <button onClick={() => dispatch({ type: 'increment' })}>Count: {count}</button>; }
在本例子中,我们使用了 useContext
来获得 context 的值。一旦 state
或 dispatch
发生更改,组件都将会重新渲染。我想提醒一下小伙们,千万不要忘了使用判断或者 useMemo 做性能优化。
当然,这仅仅只是 Reducer 的一个用例,它也可以用于处理更加复杂的状态更改逻辑。
结论
在本文中,我们介绍了使用 React 的 Context 和 Reducer 来管理状态的方式。我们创建了上下文提供者来共享数据,创建了使用上下文数据的消费者。在这些例子中,我们使用了 Reducers 作为一个状态管理器,注重解决了在更新多个操作的情况下,使用 Redux 和 Context 这样的状态管理库所需要的一些代码。
我希望此文章对您理解 React 的 Context 和 Reducer 有所帮助,它是一个更快,更干净,更简单的状态管理器。
来源:JavaScript中文网 ,转载请注明来源 https://www.javascriptcn.com/post/670c724666ef9cf37fb13fcb