React Context的使用方法及用例

457 阅读3分钟

Context提供了一个无需为每层组件手动添加props,就能在组件树间进行数据传递的方法。 Context设计目的是为了共享那些对于一个组件树而言是"全局"的状态,例如当前认证的用户,主题或者首选语言。

Api

  • createContext

    //context.js
    import {createContext} from 'createContext'
    const {ProviderConsumer} = createContext(initialValue)
    

    创建一个Context对象,initialValue为设置共享的默认数据

  • Provider

    import {Provider} from '***/context.js' //组件中引入Provider
    function App(){
    ////
    return (
            <Provider value={/* 某个值or 对象 */}>
                <div>
                //伪代码
                <Child />
                </div>
            </Provider>
        )
    }
    

    每个Context对象都返回一个Provider组件,它允许消费组件订阅context的变化,它接收一个value属性,传递给消费组件,当value发生变化时候,会跳过其子组件的shouldComponentUpdate函数传递给子组件内部的消费组件(Consumer)

  • contextType

    import React , {Componnet} from 'react' 
    import MyContext from '***/context.js'
    class Child extends Component {
        static contextType = myContext
        render(){
            const val = this.context
            return (
                <div>{val}</div>
            )
        }
    }
    deport default Child
    

    消费组件使用contextType接收数据,当从离该组件最近的祖先组件的Provider传递过来的值发生变化时,重新渲染消费组件

  • Consumer

    import React from 'react'
    import {Consumer} from '***/context.js'
    function Child(){
        return (
            <Cousumer>
                {
                    val => {
                        return (
                            <div>{val}</div>
                        )
                    }
                }
            </Cousumer>
        )
    }
    

    使用Consumer也可以订阅到context变更,但是需要在Consumer中return一个react节点,val为离这个组件最近的祖先组件的Provider提供的value值,如果没有,则为默认的initialValue

  • displayName

    import MyContext from '***/context.js'
    MyContext.displayName = 'myDisplayName'
    

    在React DevTools中对显示的context对象的名称进行更改

使用场景

  • 使用Provider和Consumer生产和消费数据

    //context.js
    import {createContext} from 'react'
    export const {Provider,Consumer}  = createContext('primary')
    

    父组件

    import React, { useState } from "react";
    import { Provider } from "./**/**/context";
    import { Card, Select } from "antd";
    import MidComponent from "./**/MidComponent";
    const {Option} = Select
    function App(){
        const [type, setType] = useState('') 
        function handleTypeChange(val){
            setType(val)  //切换类型
        }
        return (
            <Provider value ={type}>
                <Card
                    title = '更换按钮类型'
                    style={{width:300}}
                    className='card-box'
                    extra = {
                        <Select
                            placeholder:'请选择一种类型'
                            onChange={handleTypeChange}
                            >
                                <option value='primary'>primary</option>
                                <option value='default'>default</option>
                                <option value='link'>link</option>
                                <option value='dashed'>dashed</option>
                                <option value='text'>text</option>
                        </Select>
                    }
                    >
                    <MidComponent />
                </Card>
            </Provider>
        )
    }
    

    中间组件

    import React, { Component } from "react";
    import { Card } from "antd";
    import { Consumer } from "./**/context";
    import Child from "./**/Child ";
    class MidComponent extends Component {
      render() {
        return (
          <Consumer>
            {(value) => (
              <Card title={"你选择的类型是:" + value}>
                <Child />
              </Card>
            )}
          </Consumer>
        );
      }
    }
    export default MidComponent;
    

    //消费组件

    import React, { Component } from "react";
    import { Consumer } from "./**/context";
    import { Button } from "antd";
    class Child extends Component {
      render() {
        return (
          <Consumer>
            {(type) => (
              <div>
                按钮 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
                <Button type={type}>Button</Button>
              </div>
            )}
          </Consumer>
        );
      }
    }
    export default Child
    

    结果显示:

result.gif

  • Provider嵌套使用

    //context.js
    import React ,{CreateContext} from 'react'
    export const {Provider,COnsumer} = createContext()
    

    组件中使用

    import {Provider} from  './**/context.js'
    function App(){
        ....
        ///
        return (
            <Provider value='primary' />
                <div>
                    <Mid />    //中间的组件
                </div>
            <Provider>
        )
    }
    
    //Mid.js
    import {Provider} from  './**/context.js'
     function Mid(){
     ...
     ////
        return (
            <Provider value ='link'> // 里层的会覆盖外层的数据
                <div>
                    <Child />
                </div>
            </Provider>
        )
     }
    
    //Child.js
    import {COnsumer} from '/./**/context.js'
    function Child(){
        return (
            <Consumer>
                {
                    value => <Button type={value}></Button>   //link
                }
            </Consumer>
        )
    }
    
  • 消费多个Context

    //context.js
    import React, {createContext} from 'react'
    export const FirstContext = createContext()
    export const SecondContext = createContext()
    

    //订阅组件

    import {FirstContext,SecondContext} from './context.js'
    ////
    ///
    ///
    return (
        <FirstContext.Provider value={value1}>
            <SecondContext value={value2}>
                <Child />
            </SecondContext>
        </FirstContext.Provider>
    )
    

    子组件

    import {FirstContext,SecondContext} from './context.js'
    ////
    ///
    ///
    return (
        <FirstContext.Consumer>
            {
                value1 => {
                    <SecondContext.Consumer>
                        {
                            value2 => {
                                ....
                                //////
                            }
                        }
                    </SecondContext.Consumer>
                }
            }
        </FirstContext.Consumer>
    )
    

总结

  • 创建context对象唯一方式是React.createContext(initialValue)
  • 当React渲染了一个订阅这个Context对象的组件,这个组件会从最近父级的Provider中读取value值
  • 当没有匹配到Provider时,initialValue才会生效
  • Provider可以绕过其子组件中的shouldComponentUpdate函数来使得消费组件重新渲染
  • 多个Provider可以嵌套
  • contextType可以简化context使用,不使用Consumer也可以共享变量
  • contextType和Consumer功能相似,但是contextType只能在类组件中使用
  • contextType只能订阅单一context对象,可以使用this.context来消费最近的Context上的那个值