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
中返回useUserContext
ThemeProvider
中返回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
,使其成为纯组件。