React Context用法小结(附完整代码)
作者:成续源
前言
传统的React应用中,数据通过props属性自上而下(由父组件向子组件)传递,当组件层级数量增多时,在每一层传递props则很繁琐,Context提供了一种新的组件之间共享数据的方式,允许数据隔代传递,而不必显式的通过组件树逐层传递props。
根据官网链接对Context常用的5种使用场景进行了整理,并随文配上完整代码。
使用场景:
- 父组件使用Provider生产数据,子组件使用Consumer消费数据
- 子组件使用ContextType接收数据
- 动态和静态Context(父组件更新Context,被Provider包裹的子组件刷新数据,没被Provider包裹的子组件使用Context默认值)
- 在嵌套组件中更新Context(子组件通过Context传递的函数更新数据)
- 消费多个Context
知识点汇总
- 首先创建1个Context对象,当React渲染1个订阅了Context对象的组件,这个组件会从组件树中离自身最近的那个匹配的Provider读取到当前的context值。
- 每个Context都返回1个React组件,允许消费组件订阅context的变化
- ContextType只能在类组件中使用
- 1个组件如果有多个消费者,ContextType支队其中1个有效,也就是ContextType只能有1个(所以推荐使用Provider和Consumer形式)
场景1:使用Provider和Consumer生产和消费数据 文件目录及说明
以下文件在同级路径:
- ProductContext.js:创建的Context组件文件
- ProviderPage.js:父组件(数据生产者)
- MiddlePage.js:中间组件
- ConsumerPage.js:子组件(数据消费者)
数据传递路径:
ProviderPage.js ==> MiddlePage.js ==> ConsumerPage.js
代码文件 ProductContext.js:
import React from 'react'; // Context 可以让我们无须明确地传遍每一个组件,就能将值深入传递进组件树。 // 创建1个Context对象并给定默认值,如果没有匹配到Provider,消费组件取Context默认值 export const ProductContext = React.createContext({ name: 'car', price: 8000, unit: '$', }); export const { Provider, Consumer } = ProductContext;
ProviderPage.js:
import React, { PureComponent } from 'react'; import MiddlePage from './MiddlePage'; import { Provider } from './ProductContext'; class ProviderPage extends PureComponent { state = { product: { name: 'plane', price: 120000, unit: '$', }, }; render() { const { product } = this.state; return ( <div> <h1>根组件使用Provider传值,子组件使用Consumer接收</h1> <Provider value={product}> <MiddlePage /> </Provider> {/*不用Provider,显示Context对象defaultValue*/} {/* <MiddlePage />*/} </div> ); } } export default ProviderPage;
MiddlePage.js:
import React, { PureComponent } from 'react'; import ConsumerPage from './ConsumerPage'; class MiddlePage extends PureComponent { render() { return ( <ConsumerPage /> ); } } export default MiddlePage;
ConsumerPage.js:
import React, { PureComponent } from 'react'; import { Consumer } from './ProductContext'; class ConsumerPage extends PureComponent { render() { return ( <Consumer> {context => { return ( <div> name:{context.name} <br /> price:{context.price} <br /> unit:{context.unit} </div> ); }} </Consumer> ); } } export default ConsumerPage;
效果
可以看到显示的是ProviderPage组件提供的Context值。
场景2:使用ContextType接收数据
文件目录及说明
- FamilyContext.js:创建的Context组件
- FatherPage.js:父组件(生产数据)
- SonPage.js:子组件(中间组件)
- GrandSonPage.js:孙组件(消费数据)
代码文件
FamilyContext.js
import React, { PureComponent } from 'react'; import SonPage from './SonPage'; import { Provider } from './FamilyContext'; class FatherPage extends PureComponent { state = { person: { name: 'Lora', age: 99, gender: 'female', }, }; render() { const { person } = this.state; return ( <div> <h1>使用ContextType消费Context数据</h1> <Provider value={person}> <SonPage /> </Provider> {/*不用Provider,显示Context对象defaultValue*/} {/*<SonPage />*/} </div> ); } } export default FatherPage;
SonPage.js:和上面的MiddlePage.js一样
import React, { PureComponent } from 'react'; import GrandSonPage from './GrandSonPage' class SonPage extends PureComponent { render() { return ( <GrandSonPage /> ); } } export default SonPage;
GrandSonPage.js:使用ContextType
接收数据
import React, { PureComponent } from 'react'; import { FamilyContext } from './FamilyContext'; class GrandSonPage extends PureComponent { // static contextType = FamilyContext; componentDidMount() { // 使用contexType可以在任意生命周期访问数据 // 使用 this.context 来消费最近 Context 上的那个值 const value = this.context; console.log(value); } render() { // Context是1个对象,对对象进行解构操作 const { name, age, gender } = this.context; return ( <div> name:{name} <br /> age:{age} <br /> gender:{gender} </div> ); } } export default GrandSonPage;
效果
场景3:动态和静态Context
在ProviderPage.js中,被Provider
包裹的组件可以更新的Context数据,没被Provider
包裹的组件只能获取Context默认值。
代码文件
ProductContext.js
import React from 'react'; // Context 可以让我们无须明确地传遍每一个组件,就能将值深入传递进组件树。 // 创建1个Context对象 // 默认值,如果没有匹配到Provider取默认值 export const ProductContext = React.createContext({ name: 'car', price: 17000, unit: '$', }); export const { Provider, Consumer } = ProductContext;
ProviderPage.js
import React, { PureComponent } from 'react'; import { Button, Divider } from 'antd'; import MiddlePage from './MiddlePage'; import { Provider } from './ProductContext'; class ProviderPage extends PureComponent { state = { product: { name: 'plane', price: 120000, unit: '$', }, }; handleChange = () => { const { product: { name, price, unit } } = this.state; this.setState({ product: { name, price: price + 1, unit, }, }); }; render() { const { product } = this.state; return ( <div> <h1>父组件更新Context,被Provider包裹的子组件刷新值,没被包裹的子组件使用Context默认值</h1> {/*在Provider包裹的内部组件使用state中的值*/} <Provider value={product}> <MiddlePage /> </Provider> <Divider /> {/*不在Provider包裹的外部组件使用ProductContext重的默认值*/} <MiddlePage /> <Divider /> <Button onClick={this.handleChange} type="primary" > 增加 </Button> </div> // {不用Provider,显示Context对象defaultValue // <MiddlePage /> ); } } export default ProviderPage;
MiddlePage.js
import React, { PureComponent } from 'react'; import ConsumerPage from './ConsumerPage'; class MiddlePage extends PureComponent { render() { return ( <ConsumerPage /> ); } } export default MiddlePage;
ConsumerPage.js
import React, { PureComponent } from 'react'; import { Consumer } from './ProductContext'; class ConsumerPage extends PureComponent { render() { return ( <Consumer> {context => { return ( <div> name:{context.name} <br /> price:{context.price} <br /> unit:{context.unit} </div> ); }} </Consumer> ); } } export default ConsumerPage;
效果
点击增加按钮,上面的price会增加(使用Context更新值),下面的不变(使用Context默认值)。
场景4:在嵌套组件中更新Context
代码文件
ProductContext.js
import React from 'react'; // Context 可以让我们无须明确地传遍每一个组件,就能将值深入传递进组件树。 // 创建1个Context对象 // 注意第1个参数是1个object {} export const ProductContext = React.createContext({ product: { name: 'car', price: 8000, unit: '$', }, // 通过context传递1个函数,使得consumer组件更新context handlePrice: () => { }, }, ); export const { Provider, Consumer } = ProductContext;
ProviderPage.js
import React, { PureComponent } from 'react'; import MiddlePage from './MiddlePage'; import { Provider } from './ProductContext'; class ProviderPage extends PureComponent { state = { product: { name: 'plane', price: 120000, unit: '$', }, // state页包含了更新函数,因此会被传递进context provider handlePrice: () => this.handlePrice(), }; handlePrice = () => { const { product: { name, price, unit } } = this.state; this.setState({ product: { name, price: price + 1, unit, }, }); }; render() { const { product, handlePrice } = this.state; return ( <div> <h1>子组件通过context传递的函数更新context,等于在子组件中更新状态</h1> {/*注意此时传递进去的是state,包含product对象和1个函数*/} <Provider value={{ product, handlePrice }}> <MiddlePage /> </Provider> {/*不用Provider,显示Context对象defaultValue*/} {/* <MiddlePage />*/} </div> ); } } export default ProviderPage;
MiddlePage.js
import React, { PureComponent } from 'react'; import ConsumerPage from './ConsumerPage'; class MiddlePage extends PureComponent { render() { return ( <ConsumerPage /> ); } } export default MiddlePage;
ConsumerPage.js
import React, { PureComponent } from 'react'; import { Button, Divider } from 'antd'; import { Consumer } from './ProductContext'; class ConsumerPage extends PureComponent { render() { return ( <Consumer> {/*创建的Context对象*/} {/*ConsumerPage组件不仅从context获取product对象值,还获取1个handlePrice函数*/} {/*注意参数名要和Provider中传入的以及context中定义的一摸一样*/} {context => { return ( <div> name:{context.product.name} <br /> price:{context.product.price} <br /> unit:{context.product.unit} <Divider /> <Button onClick={context.handlePrice} type="primary" > 增加 </Button> </div> ); }} </Consumer> ); } } export default ConsumerPage;
效果
增加按钮在子组件ConsumerPage.js中定义,Context传递1个函数给子组件,具体的函数实现在父组件ProviderPage.js中,此处感觉还是类似React回调函数,优势就是中间组件MiddlePage不用传递任何props,包括回调函数。
场景5:消费多个Context
代码文件
MultiContext.js
import React from 'react'; const SchoolContext = React.createContext({ name: '南师附中', location: '南京', }); const StudentContext = React.createContext({ name: 'chengzhu', age: 17, }); export { SchoolContext, StudentContext };
ProviderPage.js
import React, { PureComponent } from 'react'; import MiddlePage from './MiddlePage'; import { SchoolContext, StudentContext } from './MultiContext'; class ProviderPage extends PureComponent { state = { school: { name: '清华大学', location: '北京', }, student: { name: '张云', age: 22, }, }; render() { const { school, student } = this.state; return ( <div> <h1>消费多个Context</h1> <SchoolContext.Provider value={school}> <StudentContext.Provider value={student}> <MiddlePage /> </StudentContext.Provider> </SchoolContext.Provider> </div> // 不用Provider包裹显示Context中定义的默认值 // <MiddlePage /> ); } } export default ProviderPage;
MiddlePage.js
import React, { PureComponent } from 'react'; import ConsumerPage from './ConsumerPage'; class MiddlePage extends PureComponent { render() { return ( <ConsumerPage /> ); } } export default MiddlePage;
ConsumerPage.js
import React, { PureComponent } from 'react'; import { SchoolContext, StudentContext } from './MultiContext'; class ConsumerPage extends PureComponent { render() { return ( <SchoolContext.Consumer> {school => ( <StudentContext.Consumer> {student => { return ( <div> 就读学校: {school.name} <br /> 学校位置: {school.location} <br /> 学生姓名: {student.name} <br /> 学生年龄: {student.age} </div> ); }} </StudentContext.Consumer> )} </SchoolContext.Consumer> ); } } export default ConsumerPage;
效果
可以看到传递了2个Context,分别是SchoolContext和StudentContext,子组件ConsumerPage消费了传递进来的2个Context数据。
到此这篇关于React Context用法小结(附完整代码)的文章就介绍到这了,更多相关React Context用法内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!