本文主要是写React中Context的概念以及使用,请一定搞清楚什么时候使用Context
通常来说,你会通过 props 将信息从父组件传递到子组件。但是,如果你必须通过许多中间组件向下传递
props,或是在你应用中的许多组件需要相同的信息,传递 props 会变得十分冗长和不便。Context
允许父组件向其下层无论多深的任何组件提供信息,而无需通过 props 显式传递。
顾名思义,Context的意思是上下文,在以组件为主的React中,Context的作用就如同props一样,将属性传递过去,只是Context的范围更广阔,可以实现根节点对下属多个子节点的属性传递,当然,如果想要实现全局的属性传递,Redux无疑是更适合的。
上下文使得组件能够无需通过显式传递参数的方式 将信息逐层传递。
Context 可以让父节点,甚至是很远的父节点都可以为其内部的整个组件树提供数据。
上图是官网拿过来的,很完美地解释了Context的辐射区域。
了解了Context的作用范围,接下来会创建一个如上图所述的结构来阐述Context的使用方法。
既然要讨论Context,第一步势必要创建一个Context,但是需要确定好Context的对象,也就是我们的Context的“根”要放在哪儿。一般来说,是放在我们需要读取属性的根节点,这次的Demo会放在名为Pcomponent的组件下。
创建context,我们需要使用到createContext来创建一个Context,而这个API需要传递一个默认参数,下面会传入如下的对象。
{
name: "mk",
emial: "mk@xxx.com",
department: "IT"
}
需要注意的是,我们需要将createContext返回的参数导出去,别的地方会用到。
import React, { createContext } from "react";
import Scomponent from "../Scomponent";
?
function Pcomponent() {
return (
<>
<Scomponent></Scomponent>
</>
);
}
export default Pcomponent;
?
export const userContext = createContext({
name: "mk",
email: "mk@xxx.com",
department: "IT",
});
无意外,既然有抛出,那就有接收以及使用,使用Context需要用到一个名为useContext的hook,且需要在当前的组件中引入我们刚刚创建的context。
import { useContext } from "react";
import { userContext } from "./../Pcomponent";
?
function Scomponent() {
const user = useContext(userContext);
return (
<>
<p>name:{user?.name}</p>
<p>email:{user?.email}</p>
<p>department:{user?.department}</p>
</>
);
}
export default Scomponent;
当前效果如下
看起来,我们的效果实现了,可以在a组件读取到b组件的数据,而且还不限制于是否为根组件的关系。
但是请注意,我们当前的效果并没有什么用,因为我们用一个export跟import也可以实现这么一个“初始化”的跨组件读取效果,而且这个是一个永远不会变的初始化效果
上下文之所以有用,是因为可以 提供来自其他组件的其他的、动态变化的值
用上下文 provider 包裹组件,为里面所有的组件指定一个上下文的值
我们需要使用Provider,在Pcomponent中将Scomponent包裹起来,为其指定一个上下文,且在下面的代码中,会使用到useState这个Hook,作用是方便进行数据的更新。
import React, { createContext, useState } from "react";
import Scomponent from "../Scomponent";
const initData = {
name: "mk",
email: "mk@xxx.com",
department: "IT",
};
function Pcomponent() {
const [userInfo, setUserInfo] = useState(initData);
return (
<userContext.Provider value={{ userInfo, setUserInfo }}>
<Scomponent></Scomponent>
</userContext.Provider>
);
}
export default Pcomponent;
export const userContext = createContext<any>(initData);
上述的代码中,第三行到第七行,我们将初始化的状态抽离出来,封装成一个初始化的数据。在第17行中设置了context的初始化数据。
最重要的代码在于第11行中,使用provider作为context的"载体",这告诉了React,当你在下面设置了读取上下文的时候,请使用这儿设置的userContext.且value属性为传递值,并且设置了更新的方法,以便在其他组件中可以更新根组件中的属性。
接着,在子组件中,我们也需要对我们的代码做一些小小的更新。
import { useContext } from "react";
import { userContext } from "./../Pcomponent";
?
function Scomponent() {
const user = useContext(userContext);
return (
<>
<p>name:{user?.userInfo?.name}</p>
<p>email:{user?.userInfo?.email}</p>
<p>department:{user?.userInfo?.department}</p>
<button
onClick={() => {
user.setUserInfo({
...user.userInfo,
name: user?.userInfo?.name + "1",
});
}}
>
更新数据
</button>
</>
);
}
export default Scomponent;
其中,由于使用Provider传进来的参数已经变成了两种,其中一个为数据,一个为更新数据的方法,所以在页面中,也需要进行更改。
效果如下:
现在,对于Context的使用到此结束了,你对context的使用程度应该到了可以上手的地步了。
当涉及到多个context的使用,我更建议使用redux或者是useReducer,如果单纯的使用多个Context的话,代码结构上很乱。
接着,会增加一个P2component组件,测试Context的范围。
为了方便测试,下面的代码会直接放在app.tsx中。
import React from "react";
import "./App.css";
import Pcomponent from "./components/Pcomponent";
import P2component from "./components/P2component";
?
function App() {
return (
<div className="App">
<Pcomponent></Pcomponent>
<P2component></P2component>
<hr/>
</div>
);
}
export default App;
P2component的内容如下,只是简单的引用了Scomponent。
import React from "react";
import Scomponent from "../Scomponent";
?
function P2component({ children }: any) {
return <Scomponent></Scomponent>;
}
export default P2component;
可见,由于p2component中没有提供provider,所以在下面的组件中就没有Context的效果。而出现了useContext"无效"的感觉.
在 useContext 之前,有一种更老的方法来读取上下文:
function Button() {
// 🟡 遗留方式 (不推荐)
return (
<ThemeContext.Consumer>
{theme => (
<button className={theme} />
)}
</ThemeContext.Consumer>
);
}
怎么看都觉得使用useContext更加简洁、方便…且我记得这个是以前函数组件才用的,虽然以前组件基本都用类组件…
与redux相同,并不是说一个项目中使用了provider就很高大上,在使用provider中,需要考虑是否组件结构合理,如上述的例子,一个props就可以解决了。
在开发中,我们也经常使用useReducer跟useContext进行开发,但是由于介绍reducer的话,篇幅会更长,所以就直接用useState来举例子了。
各位大佬好,我又来求关注了,希望各位大佬关注下我的公众号,冬至快乐~