文章概叙
本文主要是写React中Context的概念以及使用,请一定搞清楚什么时候使用Context
Context的介绍
通常来说,你会通过 props 将信息从父组件传递到子组件。但是,如果你必须通过许多中间组件向下传递
props,或是在你应用中的许多组件需要相同的信息,传递 props 会变得十分冗长和不便。Context
允许父组件向其下层无论多深的任何组件提供信息,而无需通过 props 显式传递。
顾名思义,Context的意思是上下文,在以组件为主的React中,Context的作用就如同props一样,将属性传递过去,只是Context的范围更广阔,可以实现根节点对下属多个子节点的属性传递,当然,如果想要实现全局的属性传递,Redux无疑是更适合的。
上下文使得组件能够无需通过显式传递参数的方式 将信息逐层传递。
Context 可以让父节点,甚至是很远的父节点都可以为其内部的整个组件树提供数据。
上图是官网拿过来的,很完美地解释了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
无意外,既然有抛出,那就有接收以及使用,使用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 包裹组件,为里面所有的组件指定一个上下文的值
我们需要使用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><buttononClick={() => {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"无效"的感觉.
古老的Consumer
在 useContext 之前,有一种更老的方法来读取上下文:
function Button() {// 🟡 遗留方式 (不推荐)return (<ThemeContext.Consumer>{theme => (<button className={theme} />)}</ThemeContext.Consumer>);
}
怎么看都觉得使用useContext更加简洁、方便…且我记得这个是以前函数组件才用的,虽然以前组件基本都用类组件…
最后的叮嘱
与redux相同,并不是说一个项目中使用了provider就很高大上,在使用provider中,需要考虑是否组件结构合理,如上述的例子,一个props就可以解决了。
在开发中,我们也经常使用useReducer跟useContext进行开发,但是由于介绍reducer的话,篇幅会更长,所以就直接用useState来举例子了。
个人博客
公众号文章链接
各位大佬好,我又来求关注了,希望各位大佬关注下我的公众号,冬至快乐~