Context 概念
- 上下文 (Context) 是 React 中一个重要的特性,它允许在组件树中共享数据,而不必通过每个级别显式地传递参数;
- 这是一种将数据传递到树中任意深度的组件的方法,无论其祖先组件是否知道该数据;
Context 特点
跨级传递数据
:Context 允许在组件树中任意深度传递数据,而不必每层手动传递 props;
避免多余的层次
:通过 Context 可以避免将数据传递到组件树中不需要它的组件;
易于管理
:Context 允许在一个地方管理数据,而不必在每个组件中手动管理;
参数介绍
createContext
-
创建一个 Context 对象,当 React 渲染一个订阅了这个 Context 对象的组件,这个组件会从组件树中离自身最近的那个匹配的 Provider 中读取到当前的 context 值;
-
只有当组件所处的树中没有匹配到 Provider 时,其 defaultValue 参数才会生效;这有助于在不使用 Provider 包装组件的情况下对组件进行测试;
-
注意:将 undefined 传递给 Provider 的 value 时,消费组件的 defaultValue 不会生效;
const MyContext = React.createContext(defaultValue);
Provider
-
每个 Context 对象都会返回一个 Provider React 组件,它允许消费组件订阅 context 的变化;Provider 接收一个 value 属性,传递给消费组件;
-
一个 Provider 可以和多个消费组件有对应关系,多个 Provider 也可以嵌套使用,里层的会覆盖外层的数据;
export const MyContext = React.createContext();
class App extends React.Component {
render () {
return (
// 在根节点提供了一个全局对象,对象连具有 color 属性
<MyContext.Provider value={{ color: 'red' }}>
<Child />
</MyContext.Provider>
);
}
}
Consumer、contextType
-
Consumer 消费
- React 组件可以订阅到 context 变更,这能在 函数式组件 中完成订阅 context;
- 这里需要 函数作为子元素 (function as child) 这种做法;
-
contextType 消费
- 在类组件中使用静态属性 contextType 接收 context;
- 这样可以在 render 以外的地方使用 context 了;
import { MyContext } from "./grandFather-context";
<MyContext.Consumer>
{({ color }) => (
// 这里可以正常返回 jsx 代码
<div style={{ color }}>
Hello World!
</div>
)}
</MyContext.Consumer>
import { MyContext } from "./grandFather-context";
class MyClass extends React.Component {
// static contextType = MyContext;
render() {
let value = this.context;
/* 基于 MyContext 组件的值进行渲染 */
}
}
MyClass.contextType = MyContext;
使用场景
主题色切换;
多国语言切换,也就是国际化;
祖孙组件之间的传值;
案例:表单
// index.js
import React from "react";
import ReactDOM from "react-dom";
import App from "./App";
ReactDOM.render(<App />, document.getElementById("root"));
// App.js
import React from "react";
import Test from "./Form/Test";
export default function App() {
return <Test />;
}
// Form/Test.js
import React, { Component } from "react";
import Form from "./index";
export default class Test extends Component {
render() {
return (
<div>
<Form onSubmit={(datas) => { console.log(datas); }} >
<div>
账号: <Form.Input name="loginId" />
</div>
<div>
密码: <Form.Input name="loginPwd" type="password" />
</div>
<div>
<Form.Button>提交</Form.Button>
</div>
</Form>
</div>
);
}
}
// Form/formContext.js
import React from "react";
const ctx = React.createContext();
export const { Provider, Consumer } = ctx;
export default ctx;
// Form/index.js
import React, { Component } from "react";
import { Provider } from "./formContext";
import FormInput from "./FormInput";
import PropTypes from "prop-types";
import FormButton from "./FormButton";
export default class Form extends Component {
state = {
formData: {}, // 表单数据对象
// 修改formData中的数据
changeFormData: (name, val) => {
this.setState({
formData: {
...this.state.formData,
[name]: val,
},
});
},
submit: () => {
this.props.onSubmit && this.props.onSubmit(this.state.formData);
},
};
static propTypes = {
onSubmit: PropTypes.func,
};
render() {
return (
<div>
<Provider value={this.state}>{this.props.children}</Provider>
</div>
);
}
}
Form.Input = FormInput;
Form.Button = FormButton;
// Form/FormInput.js
import React, { Component } from "react";
import PropTypes from "prop-types";
import ctx from "./formContext";
// 一定处于上下文中
export default class FormInput extends Component {
static contextType = ctx;
static defaultProps = {
type: "text",
};
static propTypes = {
name: PropTypes.string.isRequired, // 文本框的名称
type: PropTypes.string.isRequired, // 文本框的类型
};
render() {
return (
<input
value={this.context.formData[this.props.name] || ""}
onChange={(e) => {
this.context.changeFormData(this.props.name, e.target.value);
}}
type={this.props.type}
/>
);
}
}
// Form/FormButton.js
import React from "react";
import { Consumer } from "./formContext";
export default function FormButton(props) {
return (
<Consumer>
{(ctx) => {
return (
<button onClick={() => { ctx.submit(); }}>
{props.children}
</button>
);
}}
</Consumer>
);
}
第 5️⃣ 座大山:Observer 函数
上一篇