Context
Context
类似于Vue中的provider
和inject
,用于嵌套很深的爷孙组件之间传值,需要逐层传递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
组件所提供的数据,在根据组件的所有后辈组件中都可以使用