我们相信:世界是美好的,你是我也是。平行空间的世界里面,不同版本的生活也在继续...

本文属于React上下文context相关的createContext()useContext()钩子的复习文章,不过从代码上却审视出了一种新的现象结论,就是渲染可能并不是从上到下,一个一个变的,可以越过一些层级进行渲染变化。那么,可以引发组件到底是如何为什么会重新渲染的思考。

苏南大叔:React利用useContext()越级继承/越级执行/越级渲染现象 - useContext
React利用useContext()越级继承/越级执行/越级渲染现象(图3-1)

苏南大叔的“程序如此灵动”博客,记录苏南大叔的代码编程经验总结。本文测试环境:nodejs@20.18.0create-react-app@5.0.1react-router-dom@6.27.0react@18.3.1。定义一个上下文context,然后通过设置其value为其包裹的任意层级的组件,传递函数或数据。

使用context的基本套路

定义一个上下文,以及初始值。

const TestContext = React.createContext("初始值");

然后,定义一个接收(消费)上下文的组件。

function L2() {
  let context = useContext(TestContext);
  console.log(context);
}

context.Provider覆盖初始值

使用<.Provider value={}>可以覆盖上下文定义时的初始值,value是必填项目。

// const [token, setToken] = useState("覆盖值"); 
// 赋予数据在渲染界面里面的可变性
// const getToken = () => {};
// 赋予子代组件,修改祖辈组件数据的能力
//...
<TestContext.Provider value={{ token, setToken, getToken }}>
  <L2 />
</TestContext.Provider>

输出:

{token: '覆盖值', setConfig: ƒ, getInfo: ƒ}

支持组件无限嵌套

后代任意层次的组件,使用上下文,都可以获得传递的函数或数据。不过,这个数据是个副本。直接修改它的话,仅在本层生效。

function L1({ children }) {
  console.log("L1");
  return <>{children}</>; // 注意这里的写法,不是组件<>而是{}字符串
}

单向数据流

值得特别说明的是:
context是个单向的数据流,子组件不能修改父组件里面的context,每个子组件拿到的都是一个副本,仅在其组件内生效。
如果想子组件改父组件的值,可以在父组件里面使用useState(),然后赋予子组件对应的委托修改能力。

if (context.setToken && context.getToken) {
  context.setToken(context.getToken()); 
  // 这个能够改变渲染中的{context.token},因为属于顶层setState的范畴
  context.token = context.getToken();
  // 这个不能改变渲染中的{context.token},因为属于非reactive的自身变量
  console.log(context.token);
}

完整代码

代码一

组件不使用context.provider,也可以拿到值。

import React, { useContext } from "react";
const TestContext = React.createContext({ token: "初始值" });
function L2() {
  let context = useContext(TestContext);
  return (
    <>{context.token}</>
  );
}
export default function App() {
  return (<L2 />);
}

代码二

contextvalue可以是普通字符串,也可以是函数。

  • context.provider value={}可以改写上下文的导入值。
  • 通过useState()的导出函数,子组件可以执行函数以刷新父组件。
  • 后代组件中,不管包裹多少层,都可以根据context拿到对应数据。
  • 点击按钮后,注意:rootl2都被重新渲染了,L1同时顺次被渲染。
import React, { useCallback, useContext, useState } from "react";
const TestContext = React.createContext({ token: "初始值" });
function L1({ children }) {
  console.log("L1");
  return <>{children}</>; // 注意这里的写法,不是组件<>而是{}字符串
}
function L2() {
  console.log("L2");
  let context = useContext(TestContext);
  return (
    <>
      <button
        onClick={() => {
          if (context.setToken && context.getToken) {
            context.setToken(context.getToken());
          }
        }}
      >
        直接变换顶层状态,{context.token}
      </button>
      <br />
    </>
  );
}
export default function Root() {
  console.log("Root");
  const [token, setToken] = useState("覆盖值");
  const getToken = useCallback(() => {
    return Date.now().toString().slice(-6);
  }, []);
  return (
    <TestContext.Provider value={{ token, setToken, getToken }}>
      <L1>
        <L2 />
      </L1>
    </TestContext.Provider>
  );
}

苏南大叔:React利用useContext()越级继承/越级执行/越级渲染现象 - provider
React利用useContext()越级继承/越级执行/越级渲染现象(图3-2)

代码三【本文重点内容】

  • Context.Provider也可以包装成组件。
  • 仔细观察本地代码的输出:点击按钮后,Root被渲染,L2被渲染,但是两者之间的【L1并没有被重新渲染】。
import React, { useCallback, useContext, useState } from "react";
const TestContext = React.createContext({ token: "初始值" });
function L1({ children }) {
  console.log("L1");
  return <>{children}</>; // 注意这里的写法,不是组件<>而是{}字符串
}
function L2() {
  console.log("L2");
  let context = useContext(TestContext);
  return (
    <>
      <button
        onClick={() => {
          if (context.setToken && context.getToken) {
            context.setToken(context.getToken());
          }
        }}
      >
        直接变换顶层状态,{context.token}
      </button>
      <br />
    </>
  );
}
function Root({ children }) {
  console.log("Root");
  const [token, setToken] = useState("覆盖值");
  const getToken = useCallback(() => {
    return Date.now().toString().slice(-6);
  }, []);
  return (
    <TestContext.Provider value={{ token, setToken, getToken }}>
      {children}
    </TestContext.Provider>
  );
}
export default function App() {
  return (
    <Root>
      <L1>
        <L2 />
      </L1>
    </Root>
  );
}

苏南大叔:React利用useContext()越级继承/越级执行/越级渲染现象 - 重新再渲染被略过
React利用useContext()越级继承/越级执行/越级渲染现象(图3-3)

相关文章

如果本文对您有帮助,或者节约了您的时间,欢迎打赏瓶饮料,建立下友谊关系。
本博客不欢迎:各种镜像采集行为。请尊重原创文章内容,转载请保留作者链接。

 【福利】 腾讯云最新爆款活动!1核2G云服务器首年50元!

 【源码】本文代码片段及相关软件,请点此获取更多信息

 【绝密】秘籍文章入口,仅传授于有缘之人   react