1 使用背景
为了跨组件进行数据传递,尤其是跨多级组件传递,使用context能简化流程,且更为清晰。需要注意的是,依赖context的组件,在context发生改变的时候会更新(不受shouldComponentUpdate控制)
需要注意的是:context比较为浅比较,如果父组件传递给子组件的context是一个新对象,则会触发子组件更新。
2 类组件的使用
注意:context一定要单独存放,否则可能会有引用问题,导致无法找到context,即import加载顺序
一、声明context
// context.js
import React from 'react';
export const NameContext = React.createContext("xxx");
二、在父组件上设置Provider
// App.js
import React from "react";
import Page01 from "./routes/page01";
import { NameContext } from "./context/Context";
// export const NameContext = React.createContext("xxx"); 不要在这里创建context,需要放在单独的文件里
function App() {
return (
<NameContext.Provider value="xiaopi3">
<div>
<h1>这是一个APP!</h1>
<Page01 />
</div>
</NameContext.Provider>
);
}
export default App;
三、在后辈组件中,声明contextType,即可使用this.context
// Page01.js
import React, { Component } from 'react'
import {NameContext} from '../../context/Context';
export default class Page01 extends Component {
static contextType = NameContext;
render() {
return (
<div>
<h1>当前是一个测试页面Page01!</h1>
<div>{this.context}</div>
</div>
)
}
}
四、改变context
一般而言如果需要传入改变context的方法,需要将其值和方法作为一个对象传递给provider的value (注意:value为浅比较,如果引用未改变则不会更新页面,所以此处使用类的state,因为调用setState后会生成一个新的state)
// App.js
import React from "react";
import Page01 from "./routes/page01";
import { NameContext } from "./context/Context";
class App extends React.Component {
state = {
name: "xxx",
changeName: (name) => this.setState({ name }),
};
render() {
return (
<NameContext.Provider value={this.state}>
<div>
<h1>这是一个APP!</h1>
<Page01 />
</div>
</NameContext.Provider>
);
}
}
export default App;
使用的时候:this.context.changeName(‘pp’)即可。
3 函数组件的使用
和类组件不同,函数组件无需声明contextType,但需要在render中使用consumer:
// 类组件
static contextType = NameContext;
...
<div>{this.context}</div>
// 函数组件
import React from "react";
import { NameContext } from "../../../context/Context";
export default function Cp01() {
return (
<>
我是N代子组件
<NameContext.Consumer>
{(context) => (
<>
<div>{context.name}</div>
<button onClick={()=>context.changeName("pp")}>changeName</button>
</>
)}
</NameContext.Consumer>
</>
);
}
注意:Context.Consumer内部需要一个函数作为子元素!接收当前context值,并返回react节点。
4 消费多个context
如果组件需要消费多个context,需要使用consumer来进行嵌套,同时传递value时也需要嵌套!
// provider嵌套
<ThemeContext.Provider value={theme}>
<UserContext.Provider value={signedInUser}>
<Layout />
</UserContext.Provider>
</ThemeContext.Provider>
// consumer嵌套
<ThemeContext.Consumer>
{theme => (
<UserContext.Consumer>
{user => (
<ProfilePage user={user} theme={theme} />
)}
</UserContext.Consumer>
)}
</ThemeContext.Consumer>
5 useContext
注意事项同非hook方式一样,在context更新时,该hook会触发组件重新渲染,即使父组件使用了React.memo或shouldComponentUpdate。
该hook接收context:useContext(MyContext) 相当于 class 组件中的 static contextType = MyContext 或者 <MyContext.Consumer>
注意:useContext(MyContext) 只是让你能够读取 context 的值以及订阅 context 的变化。你仍然需要在上层组件树中使用 <MyContext.Provider>来为下层组件提供 context。
使用hook改写部分代码:
// hook方式
import React,{useContext} from "react";
import { NameContext } from "../../../context/Context";
export default function Cp01() {
const context = useContext(NameContext);
return (
<>
Cp01
<div>{context.name}</div>
<button onClick={() => context.changeName("pp")}>changeName</button>
</>
);
}