React中也许你会用到的Context

文章概叙

本文主要是写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需要传递一个默认参数,下面会传入如下的对象。

javascript 复制代码
{
  name: "mk",
  emial: "mk@xxx.com",
  department: "IT"
}

需要注意的是,我们需要将createContext返回的参数导出去,别的地方会用到。

javascript 复制代码
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。

javascript 复制代码
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,作用是方便进行数据的更新。

javascript 复制代码
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属性为传递值,并且设置了更新的方法,以便在其他组件中可以更新根组件中的属性。 接着,在子组件中,我们也需要对我们的代码做一些小小的更新。

javascript 复制代码
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中。

javascript 复制代码
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。

javascript 复制代码
import React from "react";
import Scomponent from "../Scomponent";
​
function P2component({ children }: any) {
  return <Scomponent></Scomponent>;
}
export default P2component;

可见,由于p2component中没有提供provider,所以在下面的组件中就没有Context的效果。而出现了useContext"无效"的感觉.

古老的Consumer

在 useContext 之前,有一种更老的方法来读取上下文:

javascript 复制代码
function Button() {
  // 🟡 遗留方式 (不推荐)
  return (
    <ThemeContext.Consumer>
      {theme => (
        <button className={theme} />
      )}
    </ThemeContext.Consumer>
  );
}

怎么看都觉得使用useContext更加简洁、方便...且我记得这个是以前函数组件才用的,虽然以前组件基本都用类组件...

最后的叮嘱

与redux相同,并不是说一个项目中使用了provider就很高大上,在使用provider中,需要考虑是否组件结构合理,如上述的例子,一个props就可以解决了。

在开发中,我们也经常使用useReducer跟useContext进行开发,但是由于介绍reducer的话,篇幅会更长,所以就直接用useState来举例子了。

个人博客

公众号文章链接

各位大佬好,我又来求关注了,希望各位大佬关注下我的公众号,冬至快乐~

相关推荐
GISer_Jing5 小时前
React核心功能详解(一)
前端·react.js·前端框架
FØund4047 小时前
antd form.setFieldsValue问题总结
前端·react.js·typescript·html
疯狂的沙粒8 小时前
如何在 React 项目中应用 TypeScript?应该注意那些点?结合实际项目示例及代码进行讲解!
react.js·typescript
鑫宝Code9 小时前
【React】React Router:深入理解前端路由的工作原理
前端·react.js·前端框架
沉默璇年18 小时前
react中useMemo的使用场景
前端·react.js·前端框架
红绿鲤鱼19 小时前
React-自定义Hook与逻辑共享
前端·react.js·前端框架
loey_ln21 小时前
FIber + webWorker
javascript·react.js
zhenryx1 天前
前端-react(class组件和Hooks)
前端·react.js·前端框架
老码沉思录1 天前
React Native 全栈开发实战班 - 性能与调试之打包与发布
javascript·react native·react.js
沉默璇年1 天前
react中Fragment的使用场景
前端·react.js·前端框架