函数组件 hook--useContext

Context

Context类似于Vue中的providerinject,用于嵌套很深的爷孙组件之间传值,需要逐层传递props或者state时。Context 是一种允许组件之间传递数据的方式,而无需通过props手动传递,它特别适用于那些需要在多个层级组件间共享的状态信息。

使用 React.createContext() 创建一个 Context 对象。这个对象包含两个组件:<Provider /><Consumer />Provider 用于提供状态给后代组件,而 Consumer 则用于接收这些状态。

js 复制代码
// 创建Context对象
import { createContext } from "react";
// 创建上下文组件
const objContext = createContext();
export default objContext;
// Provider提供状态给后代组件
<> 
    <h1>{this.state.msg}</h1> 
    {/* 将Provider组件包裹住子组件 */} 
    <objContext.Provider value={{ username: 'Gloria', info: this.state.passMsg }}> 
        <Son></Son> 
    </objContext.Provider>
</>
// Consumer接收组件传递的状态
<> 
    <h4> {this.state.mes} </h4> 
    {/* Consumer组件获取数据 */} 
    <objContext.Consumer> { 
        (ctxValue) => { 
            console.log(ctxValue,"son组件接收provider生产者传递的数据"); 
            return ( <> <p>{ctxValue.username}</p> </> ) 
        } 
     } 
    </objContext.Consumer> 
</>

Context.Provider包裹住一个组件后,其所有的子孙组件都可以获取到指定上下文的值。Provider(生产者),用于生产共享数据的地方。Provider只接收一个 value 属性,属性值就是需要传递给消费组件的数据。Context.Consumer组件是消费者,该组件中消费者必须使用函数接收当前的context值,然后返回一个 React 节点。传递给函数的value值就是Provider提供的value值。

useContext产生的原因

js 复制代码
// themeContext.js
import { createContext } from "react";
const themeContext = createContext();
export default themeContext;
js 复制代码
// App.js
import { useState } from 'react';
import ThemeContext from "./themeContext.js"
function ThemedButton() {
  return (
    <>
      <ThemeContext.Consumer>
        {
          (ctx) => {
            console.log(ctx,"ctx")
            return (
              <div style={{ background: ctx === 'dark' ? 'black' : 'blue',width:"100px",height:"100px" }}>
              </div>
            )
          }
        }
      </ThemeContext.Consumer>
    </>
  );
}
function App() {
  let [theme, setTheme] = useState("light");
  const changeBgc = () => {
    setTheme(prevTheme => {
      console.log(prevTheme,"prevTheme")
      if (prevTheme == "light") {
        return "dark"
      } else {
        return "light";
      }
    })
  }
  return (
    <>
      <button onClick={changeBgc}>修改背景颜色</button>
      <br />
      <br />
      <ThemeContext.Provider value={theme}>
        <ThemedButton />
      </ThemeContext.Provider>
    </>
  );
}
export default App;

在组件中只能在Consumer组件的模板中渲染Provider提供的数据,因为ctx该形参只能在return的函数内部能访问。

类组件中除了在Consumer组件中,以下地方都可以访问到:

  • constructor(props, context)
  • componentWillReceiveProps(nextProps, nextContext)
  • shouldComponentUpdate(nextProps, nextState, nextContext)
  • componetWillUpdate(nextProps, nextState, nextContext)
  • 所有能访问this的地方都可以使用this.context来获取上下文对象,就可以得到提供者的数据

而函数组件中没有this,所以引入useContext这个传递组件上下文的钩子,提供给函数组件内部中使用读取和订阅Context对象的功能。

useContext的使用

js 复制代码
import React from "react";
const MyContext = React.createContext();
const value = useContext(MyContext)

useContext接收一个Context对象为参数,该Context对象是由React.createContext函数生成的。useContext的返回值是当前 Context 中的状态。一旦在某个组件里面使用了useContext这就相当于该组件订阅了这个 Context的状态,当Context状态发生变化时,使用到该Context的子组件就会被触发重渲染,且它们会拿到Context的最新值。

创建Context对象:

js 复制代码
// themeContext.js
import { createContext } from "react";
const themeContext = createContext();
export default themeContext;

使用useContext

js 复制代码
import { useState, useContext, useCallback } from 'react';
import ThemeContext from "./themeContext.js"
function Son() {
  const GrandSon = useCallback(() => {
    // 使用useContext获取当前Provider提供的数据
    const themeCtx = useContext(ThemeContext);
    console.log(themeCtx, "GrandSon获取Context中的数据");
    const changeTheme = (val) => {
      themeCtx[1](val);
    }
    return (
      <>
        <h1 style={{ color: themeCtx[0] === 'dark' ? 'goldenrod' : 'blue' }}>GrandSon组件</h1>
        <div style={{ background: themeCtx[0] === 'dark' ? 'goldenrod' : 'blue', width: "100px", height: "100px" }}>
        </div>
        <br />
        <button onClick={() => { changeTheme("dark") }}>dark主题</button>
        <button onClick={() => { changeTheme("light") }}>light主题</button>
      </>
    )
  }, [])
  return (
    <>
      <GrandSon />
      <ThemeContext.Consumer>
        {
          (ctx) => {
            return <h1>{ctx[0]}--Son组件</h1>
          }
        }
      </ThemeContext.Consumer>
    </>
  );
}
function App() {
  let [theme, setTheme] = useState("light");
  const changeBgc = () => {
    setTheme(prevTheme => {
      if (prevTheme == "light") {
        return "dark"
      } else {
        return "light";
      }
    })
  }
  return (
    <>
      <button onClick={changeBgc}>App修改主题</button>
      <br />
      {/* 提供数据 */}
      <ThemeContext.Provider value={[theme, setTheme]}>
        <Son />
      </ThemeContext.Provider>
    </>
  );
}
export default App;

嵌套Provider组件

js 复制代码
// MsgCtx.js
import { createContext } from "react";
const msgContext = createContext();
export default msgContext;
js 复制代码
// DescriptionCtx.js
import { createContext } from "react";
const descriptionCtx = createContext();
export default descriptionCtx;
js 复制代码
// App.jsx
import { useState, useMemo, useCallback } from 'react'
import Des from './Des.jsx';
import MsgContext from './MsgCtx.js';
import DescriptionContext from "./DescriptionCtx.js"

export default function App() {
  console.log('App组件渲染了');
  let [msg, setMsg] = useState({ album: '启示录', substanceTime: "2022-08-09" });
  let [description, setDescription] = useState({ first: '致天堂的信', second: '天堂的回信' });
  // useCallback缓存一个函数,让这个函数不会每次函数组件更新都会重新创建
  const setMsgFun = useCallback((response) => {
    // response需要修改的数据
    console.log(response)
    setMsg(response);
  }, []);
  const setDesFun = useCallback((response) => {
    // response需要修改的数据
    console.log(response)
    setDescription(response)
  }, []);
  // useMemo缓存Provider的value数据,让其不会重新创建
  const msgValue = useMemo(() => {
    return [msg, setMsgFun]
  }, []);
  // useMemo缓存Provider的value数据,让其不会重新创建
  const desValue = useMemo(() => {
    return [description, setDesFun]
  }, [])
  return (
    <div>
      <h1>App组件</h1>
      {/* 将Provider组件进行嵌套 */}
      <DescriptionContext.Provider value={desValue}>
        <MsgContext.Provider value={msgValue}>
          <Des></Des>
        </MsgContext.Provider>
      </DescriptionContext.Provider>
    </div>
  )
}
js 复制代码
// Des.jsx
import { useContext } from 'react'
import DescriptionCtx from './DescriptionCtx.js';
import First from "./First.jsx"
export default function Des(props) {
    console.log("Des组件渲染了");
    let [state, setState] = useContext(DescriptionCtx);
    let changedescription = () => {
        state.first = '致天堂的信-->少年与海';
        state.second = '天堂的回信-->老年与海';
        setState({ ...state });
    };
    return (
        <div>
            <h2>Des子组件</h2>
            <p>{state.second}</p>
            <button onClick={changedescription}>修改description</button>
            <First></First>
        </div>
    )
}
js 复制代码
// First.jsx
import { useContext } from 'react'
import MsgCtx from './MsgCtx'
const First = () => {
    console.log("First组件渲染了")
    let [ctx, changeCtx] = useContext(MsgCtx);
    let change = () => {
        ctx.album = '启示录!!!';
        changeCtx({ ...ctx })
    }
    return (
        <div>
            <h3>First孙子组件</h3>
            <button onClick={change}>修改msg</button>
            <p>{ctx.album}</p>
            <p>{ctx.substanceTime}</p>
        </div>
    )
}
export default First

在根组件嵌套的Provider组件所提供的数据,在根据组件的所有后辈组件中都可以使用

相关推荐
PleaSure乐事4 小时前
【React.js】AntDesignPro左侧菜单栏栏目名称不显示的解决方案
前端·javascript·react.js·前端框架·webstorm·antdesignpro
getaxiosluo4 小时前
react jsx基本语法,脚手架,父子传参,refs等详解
前端·vue.js·react.js·前端框架·hook·jsx
阿伟来咯~7 小时前
记录学习react的一些内容
javascript·学习·react.js
吕彬-前端7 小时前
使用vite+react+ts+Ant Design开发后台管理项目(五)
前端·javascript·react.js
学前端的小朱7 小时前
Redux的简介及其在React中的应用
前端·javascript·react.js·redux·store
bysking8 小时前
【前端-组件】定义行分组的表格表单实现-bysking
前端·react.js
September_ning12 小时前
React.lazy() 懒加载
前端·react.js·前端框架
web行路人13 小时前
React中类组件和函数组件的理解和区别
前端·javascript·react.js·前端框架
番茄小酱00113 小时前
Expo|ReactNative 中实现扫描二维码功能
javascript·react native·react.js