如何使用 React Context 在组件之间共享数据
介绍
在 React 组件之间共享数据的方法有很多种。第一种方法,如果你是 React 开发人员,你可能已经知道了,就是使用props将数据从一个组件传递到另一个组件。但是当事情开始变得有点严重时,你就会知道这种方法有其自身的局限性。你可能已经尝试过使用第三方库来处理你的应用程序状态,比如 Redux 之类的库。像 Redux 这样的库来管理应用程序的状态很棒,但根据你的使用情况,它们可能有点难用。本指南将重点介绍 React 库中名为 React Context 的内置解决方案。
在组件之间共享数据
将 Props 传播给子组件
这是 React 101,展示了如何使用 props 将数据从一个组件传递到另一个组件。
const Application = () => {
const title = "Foobar";
return <Section title={title} />;
};
上面的例子描述了一个名为Application的组件的实现。它唯一做的事情就是渲染Section并传递一个设置为”Foobar”的title prop。
然后在Section的实现中你可以像这样访问这个 prop:
const Section = ({ title }) => {
return <span>{title}</span>;
};
如果你没有使用强类型语言(例如 TypeScript),但仍希望对 props 进行一些类型检查, React 团队发布了一个名为prop-types的第三方库
只需添加几行代码,您就可以指出Section组件的title属性是一个字符串,并且是必需的。您的Section.js文件可能如下所示:
import React from "react";
import PropTypes from "prop-types";
const propTypes = {
title: PropTypes.string.isRequired
};
const Section = ({ title }) => {
return <span>{title}</span>;
};
Section.propTypes = propTypes;
export default Section;
虽然这个解决方案在上面的例子中看起来不错,但当你必须将数据从一个组件传递到组件树中多个层级以下的另一个组件时,它就开始变得麻烦了。然后你最终会得到一些带有 props 的组件,它们实际上并没有自己使用,而只是传递给其他组件。
反应上下文
要在组件之间共享数据,您过去基本上必须在使用 props 和使用第三方库来管理应用状态之间做出选择。React 团队致力于开发内置解决方案,并在React 16.3.0中引入了 React Context 。
使用 React Context 共享数据
React Context 允许您通过组件树传递数据,而无需通过 props 或使用任何第三方库显式传递数据。本指南将仅重点介绍如何在功能组件中使用 React Context,因为它现在是创建 React 组件的事实上的方式。请注意,React Context 也可以在基于类的组件中使用。
它是如何工作的?
为了说明 React Context 如何帮助您管理应用中的数据,本指南将介绍一个您可能在职业生涯中已经经历过的真实用例。假设您被要求开发一款负责管理独角兽比赛的应用(您以前也遇到过这种情况,对吧?)。
在这个应用中,你有一个过去所有比赛的列表,其中有一个关于独角兽类型的过滤器,因为你知道,独角兽可能有翅膀、角,也可能没有,或者根本没有(在这种情况下,它们被称为马)。你还在其他地方有一个用于创建独角兽比赛的表单,其中一个字段是比赛中允许的独角兽类型。
由于此独角兽类型列表由全球独角兽联盟管理,因此它通过外部 API 公开。
为了简单起见,您可以在每次需要使用此类型列表时获取此 API。但这意味着您将多次触发相同的 HTTP 请求来获取相同的数据。您可以做的是,在启动应用时获取此参考数据,然后将其存储在某个地方以便以后重复使用。
这就是 React Context 有趣的地方。
首先创建一个ReferenceDataContext.js文件。
import { createContext, useState } from "react";
const [unicornTypes, setUnicornTypes] = useState(undefined);
ReferenceDataContext = createContext({ unicornTypes, setUnicornTypes });
const ReferenceDataContextProvider = ({ children }) => {
return (
<ReferenceDataContext.Provider value={{ unicornTypes, setUnicornTypes }}>
{...children}
</ReferenceDataContext.Provider>
);
};
export { ReferenceDataContext, ReferenceDataContextProvider };
为了创建 Context,React 公开了一个名为createContext的 API 。此函数采用 Context 的默认状态并返回一个 Context 对象。每个 Context 对象都带有一个 Provider React 组件,允许其所有子组件订阅此 Context。
这里创建了一个小型的ReferenceDataContextProvider组件,它只是用刚从createContext调用中获得的 Context Provider 包装了它的子组件。
由于这是您可能需要从应用程序的任何位置访问的参考数据,因此您必须使用此提供程序包装整个应用程序。假设您有一个应用程序组件,它是您的顶级组件。
import { useContext }, React from "react";
import { ReferenceDataContext, ReferenceDataContextProvider } from "./ReferenceDataContext"
import Main from " ./Main"
const App = () => {
const { unicornTypes } = useContext(ReferenceDataContext)
return (
<ReferenceDataContextProvider>
<Main />
</ReferenceDataContextProvider>
)
}
export default App
在这里,应用程序被这个提供程序包装在顶层,因为它公开的数据可能在应用程序的任何地方都需要,但你可以想象,如果需要,提供程序只会包装应用程序的子树。这实际上是一个好主意,本指南稍后会解释这一点。
在Main组件中,从 API 中获取类型并将其存储在 Context 中。为了稍微简化语法,假设您已在 npm 依赖项中安装了axios库来处理 HTTP 请求,但您可以随意使用适合您需求的任何解决方案。
import { useContext, useEffect, useState }, React from "react"
import { ReferenceDataContext } from "./ReferenceDataContext"
import axios from "axios"
const Main = () => {
const [isLoading, setIsLoading] = useState(false);
const { unicornTypes, setUnicornTypes } = useContext(ReferenceDataContext);
useEffect(() => {
setIsLoading(true)
axios.get("PATH_TO_THE_API")
.then(response => response.data)
.then(setUnicornTypes)
.finally(() => setIsLoading(false))
}, [axios, setUnicornTypes])
const renderTypesList = () => {
unicornTypes.map(type => <li>{ type }</li>)
}
return (
<>
{isLoading && <p>Loading...</p>}
{ !isLoading && (
<ul>
{renderTypesList()}
</ul>
) }
</>
)
}
export default Main
在此文件中,当从 API 获取类型时,您将把它们存储在上下文中。当我们列出它们时,您也在组件的渲染部分使用这些类型。当组件从上下文值导入一些数据时,它会订阅上下文值,当类型列表更新时,它会自动重新渲染。
注意事项
React Context 是一个很棒的 API,如前所述,在应用中增加多个 Provider 而不是只使用一个全局 Provider 来存储所有数据是一个好主意。您可能想存储所有数据,并使用 Provider 包装整个应用,将所有这些数据提供给应用。这种方法的主要问题是,每次传递给 Provider 的值发生变化时,订阅 Context 的每个组件都会重新渲染。这意味着,当您仅更新 Context 中的一个值时,最终可能会有大量额外的渲染。
解决方法是为每个功能部分设置专用的上下文/提供程序,并在其自己的组件之间共享数据。这样,您将减少渲染组件的数量,因为您只需更新应用程序的子树。
结论
在本指南中,我们探讨了如何轻松使用 React Context 而不是传递 props 来在组件之间共享数据。根据您的用例,您可能更喜欢使用简单的 props、React Context 甚至像 Redux 这样的第三方库在组件之间共享数据。这三种解决方案非常有效且可用于生产环境。
更多信息
- React Context 官方文档
- 使用 prop-types 进行类型检查
- 本文使用了一些 React hooks,这里是它们的文档
免责声明:本内容来源于第三方作者授权、网友推荐或互联网整理,旨在为广大用户提供学习与参考之用。所有文本和图片版权归原创网站或作者本人所有,其观点并不代表本站立场。如有任何版权侵犯或转载不当之情况,请与我们取得联系,我们将尽快进行相关处理与修改。感谢您的理解与支持!
请先 登录后发表评论 ~