react学习—新版Context

一、新版Context

旧版API存在严重的效率问题,并且容易导致滥用

1、创建上下文

上下文是一个独立于组件的对象,该对象通过React.createContext(默认值)创建

返回的是一个包含两个属性的对象

  1. Provider属性:生产者。一个组件,该组件会创建一个上下文,该组件有一个value属性,通过该属性,可以为其数据赋值
import React, { Component } from 'react'
const ctx = React.createContext({
  a: 0,
  b: 'abc'
})

export default class NewContext extends Component {
  render() {
    const Provider = ctx.Provider;
    return (
      <Provider value={}>//value类似旧版的上下文中创建的改变值的方法
        <div></div>
      </Provider>
    )
  }
}

2、使用上下文

  1. 在类组件中,直接使用this.context获取上下文数据
    1. 要求:必须拥有静态属性 contextType , 应赋值为创建的上下文对象
  2. 在函数组件中,需要使用Consumer来获取上下文数据
    1. Consumer是一个组件
    2. 它的子节点,是一个函数(它的props.children需要传递一个函数)

使用时可以直接将state状态给上下文。

import React, { Component } from 'react'
const ctx = React.createContext();

function ChildA(props) {
  return <div>
    <h1>ChildA</h1>
    <ChildB />
  </div>
}

class ChildB extends React.Component {
  static contextType = ctx;//使用上下文时时必须要有contextType
  render() {
    return <p>
      ChildB,上下文中的数据a:{this.context.a}
    </p>
  }
}

export default class NewContext extends Component {
  state = {
    a: 0,
    b: 'abc'
  }
  render() {
    const Provider = ctx.Provider;
    return (
      <Provider value={this.state}>
        <div>
          <ChildA />
        </div>
      </Provider>
    )
  }
}

在这里插入图片描述

和旧版类似,我们可以改变上下文中的值。

1.类组件中使用

import React, { Component } from 'react'
const ctx = React.createContext();

function ChildA(props) {
  return <div>
    <h1>ChildA</h1>
    <ChildB />
  </div>
}
class ChildB extends React.Component {
  static contextType = ctx;
  render() {
    return <p>
      ChildB,上下文中的数据a:{this.context.a}
      <button onClick={()=>{
        this.context.changeA(this.context.a + 2)
      }}>子组件,a+1</button>
    </p>
  }
}
export default class NewContext extends Component {
  state = {
    a: 0,
    b: 'abc',
    changeA: (newA) =>{
      this.setState({
        a: newA
      })
    }
  }
  render() {
    const Provider = ctx.Provider;
    return (
      <Provider value={this.state}>
        <div>
          <ChildA />
          <button onClick={()=>{
            this.setState({
              a: this.state.a+1
            })
          }}>父组件,a+1</button>
        </div>
      </Provider>
    )
  }
}

在这里插入图片描述

2.函数组件中使用

function ChildA(props) {
  return <div>
    <h1>ChildA</h1>
    <p>
      <ctx.Consumer>
        {value=><>子组件A,{value.a}</>}
      </ctx.Consumer>
    </p>
    <ChildB />
  </div>
}
//或者使用children语法
function ChildA(props) {
  return <div>
    <h1>ChildA</h1>
    <p>
      {/* <ctx.Consumer>
        {value=><>子组件A,{value.a}</>}
      </ctx.Consumer> */}
      <ctx.Consumer children={value=><>子组件A,{value.a}</>}>
      </ctx.Consumer>
    </p>
    <ChildB />
  </div>
}

在这里插入图片描述
当然,类组件中也可以使用Consumer

class ChildB extends React.Component {
  static contextType = ctx;
  render() {
    return (
      <ctx.Consumer>
        {value=>(
          <p>
            ChildB,上下文中的数据a:{value.a}
            <button onClick={()=>{
              this.context.changeA(value.a + 2)
            }}>子组件,a+1</button>
          </p>
        )}
      </ctx.Consumer>
    )
  }
}

3、多个上下文

新的context将上下文与组件分离,那么我们想用那个上下文中数据,就很方便了。

import React, { Component } from 'react'
const ctx1 = React.createContext();
const ctx2 = React.createContext();

function ChildA(props) {
  return (
    <ctx2.Provider value={{
      x: 999,
      y: 'xyz'
    }}>
      <div>
        <h1>ChildA</h1>
        <p>
          <ctx1.Consumer>
            {value=><>子组件A,{value.a}</>}
          </ctx1.Consumer>
        </p>
        <ChildB />
      </div>
    </ctx2.Provider>
  )
}
class ChildB extends React.Component {
  static contextType = ctx1;
  render() {
    return (
      <ctx1.Consumer>
        {value=>(
          <>
            <p>
              ChildB,上下文中的数据a:{value.a}
              <button onClick={()=>{
                this.context.changeA(value.a + 2)
              }}>子组件,a+1</button>
            </p>
            <p>
              <ctx2.Consumer>
                {val => (
                  <>
                    上下文ctx2的数据:x:{val.x}
                  </>
                )}
              </ctx2.Consumer>
            </p>
          </>
        )}
      </ctx1.Consumer>
    )
  }
}

export default class NewContext extends Component {
  state = {
    a: 0,
    b: 'abc',
    changeA: (newA) =>{
      this.setState({
        a: newA
      })
    }
  }
  render() {
    const Provider = ctx1.Provider;
    return (
      <Provider value={this.state}>
        <div>
          <ChildA />
          <button onClick={()=>{
            this.setState({
              a: this.state.a+1
            })
          }}>父组件,a+1</button>
        </div>
      </Provider>
    )
  }
}

在这里插入图片描述
注意细节

如果,上下文提供者(Context.Provider)中的value属性发生变化(Object.is比较),会导致该上下文提供的所有后代元素全部重新渲染,无论该子元素是否有优化(无论shouldComponentUpdate函数返回什么结果)

如下代码(每次使用setState,尽管没有任何操作,也会提供一个新的state

import React, { Component } from 'react'

const ctx = React.createContext();
class ChildB extends React.Component {

    static contextType = ctx;

    shouldComponentUpdate(nextProps, nextState) {
        console.log("运行了优化")
        return false;
    }


    render() {
        console.log("childB render");
        return (
            <h1>
                a:{this.context.a},b:{this.context.b}
            </h1>
        );
    }
}

export default class NewContext extends Component {

    state = {
        a: 0,
        b: "abc",
        changeA: (newA) => {
            this.setState({
                a: newA
            })
        }
    }

    render() {
        return (
            <ctx.Provider value={this.state}>
                <div>
                    <ChildB />
                    <button onClick={() => {
                        this.setState({
                          a: this.state.a+1
                        })
                    }}>父组件的按钮,a加1</button>
                </div>
            </ctx.Provider>
        )
    }
}

当上下文改变,我们发现生命周期函数并没有运行,而是直接重新渲染,这就是“强制更新
在这里插入图片描述
那么在一些组件中我们需要使用生命周期函数进行性能优化,如何避免强制更新呢?

我们将需要用到的向下文ctx保存在state属性中,虽然每次调用setState函数state都会更新一次,但是内部的对象仍然指向的是同一个地址,所以就不会强制更新了

import React, { Component } from 'react'

const ctx = React.createContext();
class ChildB extends React.Component {

    static contextType = ctx;

    shouldComponentUpdate(nextProps, nextState) {
        console.log("运行了优化")
        return false;
    }


    render() {
        console.log("childB render");
        return (
            <h1>
                a:{this.context.a},b:{this.context.b}
            </h1>
        );
    }
}

export default class NewContext extends Component {

    state = {
        ctx: {
            a: 0,
            b: "abc",
            changeA: (newA) => {
                this.setState({
                    a: newA
                })
            }
        }
    }

    render() {
        return (
            <ctx.Provider value={this.state.ctx}>
                <div>
                    <ChildB />

                    <button onClick={() => {
                        this.setState({})
                    }}>父组件的按钮,a加1</button>
                </div>
            </ctx.Provider>
        )
    }
}

在这里插入图片描述
博主开始运营自己的公众号啦,感兴趣的可以关注“飞羽逐星”微信公众号哦,拿起手机就能阅读感兴趣的博客啦!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 4
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

飞羽逐星

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值