You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
import{ThemeProvider}from'./ThemeContext';constAppProviders=({ children })=>{return<ThemeProvider>{children}</ThemeProvider>}exportdefaultAppProviders;
// src/contexts/TodoContext.jsimportReact,{useReducer,useContext}from"react";importreducer,{initialState}from"../reducers/todos-reducer";constTodoContext=React.createContext(null);exportconstTodoProvider=({ children })=>{const[state,dispatch]=useReducer(reducer,initialState);constcontext={
state,
dispatch
}return<TodoContext.Providervalue={context}>{children}</TodoContext.Provider>}exportconstuseTodo=()=>{constcontext=useContext(TodoContext);returncontext;};
// src/contexts/index.jsimport{TodoProvider}from'./TodoContext';constAppProviders=({ children })=>{return<TodoProvider>{children}</TodoProvider>}exportdefaultAppProviders;
useReducer 是 useState 的替代方案,用来处理复杂的状态或逻辑。当与其它 Hooks(useContext)结合使用,有时也是一个好的选择,不需要引入一些第三方状态管理库,例如 Redux、Mobx。
目标
在本文结束时,您将了解:
什么是 Context?
Context 解决了跨组件之间的通信,也是官方默认提供的一个方案,无需引入第三方库,适用于存储对组件树而言是全局的数据状态,并且不要有频繁的数据更新。例如:主题、当前认证的用户、首选语言。
使用 React.createContext 方法创建一个上下文,该方法接收一个参数做为其默认值,返回 MyContext.Provider、MyContext.Consumer React 组件。
MyContext.Provider 组件接收 value 属性用于传递给子组件(使用 MyContext.Consumer 消费的组件),无论嵌套多深都可以接收到。
将我们的内容包装在 MyContext.Consumer 组件中,以便订阅 context 的变更,类组件中通常会这样写。
以上引入不必要的代码嵌套也增加了代码的复杂性,React Hooks 提供的 useContext 使得访问上下文状态变得更简单。
以上我们对 Context 做一个简单了解,更多内容参考官网 Context、useContext 文档描述,下面我们通过两个例子来学习如何使用 useContext 管理全局状态。
useState + useContext 主题切换
本节的第一个示例是使用 React hooks 的 useState 和 useContext API 实现暗黑主题切换。
实现 Context 的 Provider
在 ThemeContext 组件中我们定义主题为 light、dark。定义 ThemeProvider 在上下文维护两个属性:当前选择的主题 theme、切换主题的函数 toggleTheme()。
通过 useContext hook 可以在其它组件中获取到 ThemeProvider 维护的两个属性,在使用 useContext 时需要确保传入 React.createContext 创建的对象,在这里我们可以自定义一个 hook useTheme 便于在其它组件中直接使用。
代码位置:
src/contexts/ThemeContext.js
。创建一个 AppProviders,用来组装创建的多个上下文。代码位置:
src/contexts/index.js
。实现 ToggleTheme 组件
在 App.js 文件中,将 AppProviders 组件做为根组件放在最顶层,这样被包裹的组件都可以使用 AppProviders 组件提供的属性。
代码位置:
src/App.js
。在 ToggleTheme 组件中,我们使用自定义的 useTheme hook 访问 theme 对象和 toggleTheme 函数,以下创建了一个简单主题切换,用来设置背景颜色和文字颜色。
代码位置:
src/components/ToggleTheme.jsx
。Demo 演示
示例代码地址:https://github.com/qufei1993/react-state-example/tree/usestate-usecontext-theme。
useReducer + useContext 实现 Todos
使用 useReducer 和 useContext 完成一个 Todos。这个例子很简单,可以帮助我们学习如何实现一个简单的状态管理工具,类似 Redux 这样可以跨组件共享数据状态。
reducer 实现
在
src/reducers
目录下实现 reducer 需要的逻辑,定义的 initialState 变量、reducer 函数都是为 useReducer 这个 Hook 做准备的,在这个地方需要都导出下,reducer 函数是一个纯函数,了解 Redux 的小伙伴对这个概念应该不陌生。Context 跨组件数据共享
定义 TodoContext 导出
state
、dispatch
,结合 useContext 自定义一个 useTodo hook 获取信息。实现 Todos 组件
在 TodoAdd、Todo、Todos 三个组件内分别都可以通过 useTodo() hook 获取到 state、dispatch。
Demo 演示
上面代码实现需求是没问题,但是存在一个性能问题,如果 Context 中的某个熟悉发生变化,所有依赖该 Context 的组件也会被重新渲染,观看以下视频演示:
示例代码地址:https://github.com/qufei1993/react-state-example/tree/usereducer-usecontext-todos。
Context 小结
useState/useReducer 管理的是组件的状态,如果子组件想获取根组件的状态一种简单的做法是通过 Props 层层传递,另外一种是把需要传递的数据封装进 Context 的 Provider 中,子组件通过 useContext 获取来实现全局状态共享。
Context 对于构建小型应用程序时,相较于 Redux,实现起来会更容易且不需要依赖第三方库,同时还要看下适用场景。在官网也有说明,适用于存储对组件树而言是全局的数据状态,并且不要有频繁的数据更新(例如:主题、当前认证的用户、首选语言)。
以下是使用 Context 会遇到的几个问题:
<ThemeProvider> <UserProvider> ... </UserProvider> </ThemeProvider>
是不是有点之前 “callback 回调地狱” 的意思了。 这里有个解决思路是创建一个 store container,参考 The best practice to combine containers to have it as "global" state、Apps with many containers。在我们实际的 React 项目中没有一个 Hook 或 API 能解决我们所有的问题,根据应用程序的大小和架构来选择适合于您的方法是最重要的。
介绍完 React 官方提供的状态管理工具外,下一节介绍一下社区状态管理界的 “老大哥 Redux”。
Reference
The text was updated successfully, but these errors were encountered: