Context
上下文:做某一些事情的环境
React上下文的特点:
- 当某个组件创建上下文后,上下文中的数据会被所有后代组件共享;
- 如果某个组件依赖了上下文,会导致该组件不再纯粹(数据仅来自于属性props)
旧版API
创建上下文
只有类组件才能创建上下文
- 给类组件书写一个静态属性
childContextTypes,对上下文中的数据类型进行约束;
- 添加实例方法
getChildContext,该方法返回的对象就是上下文中的数据,该数据必须满足类型约束,该方法会在每次render之后运行
使用上下文中的数据
要求:如果要使用上下文中的数据,组件中必须有一个静态属性contextTypes,该属性描述了需要获取的上下文中的数据类型
- 可以在组件的构造函数中,通过构造函数的第二个参数,获取上下文数据
- 从组件的context属性中获取
- 在函数组件中通过第二个参数获取上下文数据
上下文中的数据变化
上下文中的数据不可以直接变化,最终都是通过状态改变
后代组件想要修改上下文中的数据,可以通过调用上下文中的函数来修改。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91
| import React, { Component } from "react"; import PropTypes from "prop-types";
function CompA(props, context) { return ( <> CompA <h2>name:{context.name}</h2> <h2>age:{context.age}</h2> <p> <button onClick={()=>{ context.onChange && context.onChange("age",context.age+1) }}>年龄加一</button> </p> </> ); }
CompA.contextTypes = { name: PropTypes.string, age: PropTypes.number, onChange: PropTypes.func, };
class CompB extends Component { static contextTypes = { name: PropTypes.string, age: PropTypes.number, onChange: PropTypes.func, }; render() { return ( <> <CompA></CompA> CompB <h2>name:{this.context.name}</h2> <h2>age:{this.context.age}</h2> <p> <button onClick={()=>{ this.context.onChange && this.context.onChange("name","谢永强") }}>修改名字为中文名</button> </p> </> ); } }
export default class OldApi extends Component { state = { name: "xyq", age: 21, };
static childContextTypes = { name: PropTypes.string, age: PropTypes.number, onChange: PropTypes.func, }; getChildContext() { return { name: this.state.name, age: this.state.age, onChange: (key, newData) => { this.setState({ [key]: newData, }); }, }; } render() { return ( <div> <CompB></CompB> </div> ); } }
|
新版API
旧版API存在效率问题,并且容易导致滥用
创建上下文
上下文是一个独立于组件的对象,该对象通过React.createContext(默认值)创建
返回的是一个包含两个属性的对象
- Provider属性:生产者。一个组件,该组件会创建一个上下文,该组件有一个value属性,通过value为其赋值,同一个Provider不要运用到多个组件中
- Consumer属性:消费者。一个组件,它的子节点是一个函数(props.children需要传递一个函数),会将上下文数据作为函数的参数,会将函数的返回值渲染出来
使用上下文中的数据
- 在类组件中,可以直接通过this.context获取上下文数据,必须要有静态属性
contextType,应该赋值为创建的上下文对象;也可以使用Consumer获取数据
- 在函数组件中,需要使用Consumer获取上下文数据
- Consumer是一个组件
- 它的子节点是一个函数(props.children需要传递一个函数),会将上下文数据作为函数的参数,会将函数的返回值渲染出来
如果上下文提供者(Context.Provider)中的value属性发生变化,会导致该上下文提高的所以后代元素全部重新渲染,无论孩子元素是否存在优化。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63
| import React, { Component } from "react"; import PropTypes from "prop-types";
const ctx = React.createContext();
class CompA extends Component { static contextType = ctx; render(){ return ( <> CompA <h2>name:{this.context.name}</h2> <h2>age:{this.context.age}</h2> <p> <button onClick={()=>{ this.context.onChange && this.context.onChange("age",this.context.age+1) }}>年龄加一</button> </p> </> ); } }
class CompB extends Component { static contextTypes = { name: PropTypes.string, age: PropTypes.number, onChange: PropTypes.func, }; render() { return ( <> <CompA></CompA> CompB </> ); } }
export default class NewApi extends Component { state = { name: "xyq", age: 21, onChange: (key, newData) => { this.setState({ [key]: newData, }); }, }; render() { const Provider = ctx.Provider; return ( <Provider value={this.state}> <CompB></CompB> </Provider> ); } }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64
| import React, { Component } from "react"; import PropTypes from "prop-types";
const ctx = React.createContext();
function CompA(props) { return ( <ctx.Consumer> {(value) => ( <> CompA <h2>name:{value.name}</h2> <h2>age:{value.age}</h2> <p> <button onClick={() => { value.onChange && value.onChange("age", value.age + 1); }} > 年龄加一 </button> </p> </> )} </ctx.Consumer> ); }
class CompB extends Component { static contextTypes = { name: PropTypes.string, age: PropTypes.number, onChange: PropTypes.func, }; render() { return ( <> <CompA></CompA> CompB </> ); } }
export default class NewApi extends Component { state = { name: "xyq", age: 21, onChange: (key, newData) => { this.setState({ [key]: newData, }); }, }; render() { const Provider = ctx.Provider; return ( <Provider value={this.state}> <CompB></CompB> </Provider> ); } }
|