Context Hooks - useContext
React程序中传递数据的主要方式是通过props传递,就像火车网络一样,props允许我们在整个应用程序中传递状态和其它数据。
但随着应用程序复杂度的提升,不同组件之间的数据传递经常需要通过多层嵌套的props传递,这不仅使得代码结构变得复杂,而且难以维护。为了解决这个问题,React提供了第二种传递数据的方式: Context上下文
Context有点像特快列车。它允许我们”跳过“某些站点,并直接从一个组件跳到另一个组件。
在这篇文章中我们将学习有关它的一切。
The Problem
在React应用程序中,有时候我们会遇到这样的情况:在顶层组件(例如App)中定义了一些数据(比如用户信息user),并且这些数据需要被多个深层次的子组件(如Profile和Header)所共享。在这种情况下,
我们首先想到的就是通过逐级传递props的方式来实现。
上述流程的真实代码如下:
随着React应用程序的发展,组件结构可能变得越来越复杂,尤其是在大型项目中,不同组件之间共享状态或数据的需求也随之增加。传统的逐级传递props的方式在处理简单的数据流时尚可行,但当组件层级加深时,这种方法往往会导致代码冗余和难以维护的问题。开发者们不得不在多个中间组件(例如: Dashboard)中添加额外的props,即使这些组件本身并不直接使用这些数据,只是为了将它们传递给更深层次的子组件。
为了解决这一问题,React引入了Context上下文机制。Context提供了一种在组件树中高效传递数据的方式,无论组件层级有多深,都可以轻松访问到这些数据,而无需通过中间组件的props进行逐级传递。这种机制极大地简化了数据管理,使得代码更加整洁,也降低了维护成本。
Syntax
接着上面的案例,让我们使用Context上下文来替换逐级透穿props
Step 1: Context
首先,我们需要创建一个新的Context,这个Context将用来存储和分发user信息。
Context可以被视为一个频道,一个我们可以用来向应用程序内部广播数据的无线电频率。它是我们用来传递数据的载体。
Step 2: Provider
在App组件中,我们使用UserContext.Provider来包裹Dashboard组件,并通过value属性传入user对象。这意味着Dashboard及其所有子组件现在都可以访问到user信息。
Step 3: Consumer
在Dashboard、Header和Profile等组件中,我们可以使用useContext Hook来直接访问user信息,而不需要从props中获取。
完整代码如下:
通过采用这种方式,我们可以有效地避免在组件树中重复传递user对象,同时保持代码的整洁和易于维护。
最佳实践
组织代码
- 将
Context逻辑提取到Provider Component中,- user 相关上下文提取到
UserProvider中 - theme相关上下文提取到
ThemeProvider中
- user 相关上下文提取到
- 在
Provider Component中返回useContext的自定义版本UserProvider中返回useUserContextThemeProvider中返回useThemeContext
- 在
Consumer中直接使用自定义hook
优化后的代码如下:
性能优化
在上一篇文章中,我们学习了如何使用React.memo创建纯组件。一个纯组件只会在 props 或 state 发生变化时才会重新渲染。
但是,当一个纯组件消费了Context会发生什么呢?比如:
使用React.memo包装Profile得到了一个纯组件,这个组件在什么情况下会重新渲染呢?
本质上来说,你可以把Context理解为internal props,它和普通的props没有任何区别,当Context中的value发生变化时,消费了这个上下文值的组件也会重新渲染。
它在功能上等同于:
因此,为避免消费了Context的Consumner组件无效渲染,可以:
- 使用
React.useMemo对Context的value进行缓存。 - 使用
React.memo包装Consumner,使其成为纯组件。