React - context

context

1.组件创建上下文之后,上下文中的数据会被所有的后代组件共享

2.组件依赖上下文,会导致组建不再纯粹依赖state和props

旧的api

创建上下文

只有类组件才可以创建上下文

1.给类组件书写静态属性childContextTypes,使用该属性对上下文中的数据类型进行约束

2.添加实例方法 getChildContext,该方法返回的对象,即为上下文中的数据,该数据必须满足类型的约束,该方法会在每次render之后运行。

const types = {
  a: PropTypes.number,
  b: PropTypes.string
};
export default class OldContext extends React.Component {
    
  static childContextTypes = types;

 state = {
    a: 0
  };

  getChildContext() {
      console.log("获取context");
    return {
      a: 111,
      b: "abc"
    };
  }
  render() {
    return (
      <div>
        <p>hello word</p>
            <button
          onClick={() => {
            this.setState({
              a: this.state.a + 1
            });
          }}
        >
          click
        </button>
      </div>
    );
  }
}

经测试发现,getChildContext方法是在render函数执行之后触发

在这里插入图片描述

使用上下文中的数据

要求:组件使用上下文中的数据,组件中必须有一个静态属性contextTypes,该属性描述了上下文中需要使用的数据类型

获取:1.可以在组件的构造函数中,通过第二参数,获取上下文数据;

​ 2.从组件的context属性中获取(this.context)

​ 3. 函数组件中,通过第二参数获取上下文

// 函数组件的获取方式
function ChildA(props, context) {
  return (
    <div>
      <h1>我是组件childA</h1>
      <p>content中的a:{context.a}</p>
      <ChildB />
    </div>
  );
}
ChildA.contextTypes = {
  a: types.a
};

// 类组件的两种方式:
// 1.直接通过this.context属性获取
class ChildB extends React.Component {
  static contextTypes = types;
  render() {
    return (
      <div>
        <h1>我是组件B</h1>
        <p>content中的a:{this.context.a}</p>
        <p>content中的b:{this.context.b}</p>
        <ChildC />
      </div>
    );
  }
}

//2.通过constructor构造函数的第二参数获取
class ChildC extends React.Component {
  static contextTypes = types;

  constructor(props, context) {
    super(props, context);
    console.log(context);
  }

  render() {
    return (
      <div>
        <h1>我是组件C</h1>
        <p>content中的a:{this.context.a}</p>
        <p>content中的b:{this.context.b}</p>
      </div>
    );
  }
}

上下文中的数据变化

上下文中的数据不可以直接更改,通过状态发送变化的而发生改变

const types = {
  a: PropTypes.number,
  b: PropTypes.string
};
export default class OldContext extends React.Component {
  state = {
    a: 11
  };
  static childContextTypes = types;

// 将context中的a的值,用state.a控制
//    当改变State改变重新触发render函数,会重新获取一次context的值
  getChildContext() {
    return {
      a: this.state.a,  
      b: "abc"
    };
  }

  render() {
    return (
      <div>
        <p>hello word</p>
        <ChildA />
        <button
          onClick={() => {
            this.setState({
              a: this.state.a + 1
            });
          }}
        >
          click
        </button>
      </div>
    );
  }
}

子组件中改变上下文中的数据:在上下文中的一个处理函数,可以用于后代更改上下文中数据(改变创建上下问的组件的状态)

// 在创建上下问的父组件中添加一个函数,用以改变其State状态

const types = {
  a: PropTypes.number,
  b: PropTypes.string,
  onChange: PropTypes.func
};
export default class OldContext extends React.Component {
  state = {
    a: 11
  };
  static childContextTypes = types;

  getChildContext() {
    return {
      a: this.state.a,
      b: "abc",
      onChange: (name, value) => { // 改变状态
        this.setState({
          [name]: value
        });
      }
    };
  }

  render() {
    return (
      <div>
        <p>hello word</p>
        <ChildA />
        <button
          onClick={() => {
            this.setState({
              a: this.state.a + 1
            });
          }}
        >
          click
        </button>
      </div>
    );
  }
}

// 以函数组件为例
function ChildA(props, context) {
  return (
    <div>
      <h1>我是组件childA</h1>
      <p>content中的a:{context.a}</p>
      <button
        onClick={() => {
          context.onChange("a", context.a + 1);
        }}
      >
        改变context值
      </button>
      <ChildB />
    </div>
  );
}

ChildA.contextTypes = {
  a: types.a,
  onChange: types.onChange
};

新版 api

旧版存在严重的效率问题,容易滥用

创建上下文

上下文是一个独立于组件的对象,该对象通过React.createContext(default)创建,返回的是一个包含两个属性的对象

1.Provider属性:一个组建,改组件会创建一个上下文,该组建有一个value属性,为上下文赋值

​ 同一个Provider属性,不要用到多个组件中,如果需要在其他组件中使用该数据,应该考虑将数据提升到更高的层次

2.Consumer属性

使用上下文中的数据

1.在类组件中,直接使用this.context获取上下文数据

​ 要求:必须有一个静态属性contextTypes,应赋值为创建的上下文对象

2.在函数组件(或类组件)中,需要使用Consumer来获取上下文数据

​ 1.Consumer是一个组件

2.它的子节点,是一个函数(上下文作为该函数的参数,返回值会被渲染到页面)

完整示例

import React, { Component } from "react";

const ctx = React.createContext();

export default class NewContext extends Component {
  state = {
    a: 11,
    onChange: newA => {
      this.setState({
        a: newA
      });
    }
  };
  render() {
    return (
      <ctx.Provider value={this.state}>
        <button
          onClick={() => {
            this.setState({
              a: this.state.a + 1
            });
          }}
        >
          父组件改变state值
        </button>
        <p>NewContext组件:</p>
        <ChildA />
        <ChildB />
        <ChildC />
      </ctx.Provider>
    );
  }
}

// 类组件有两种方式
// 1. ----------------------------
class ChildA extends Component {
  static contextType = ctx;
  render() {
    return (
      <div>
        <p>组件ChildA:</p>
        <p>context:{this.context.a}</p>
        <button
          onClick={() => {
            this.context.onChange(this.context.a + 1);
          }}
        >
          组件改变context值
        </button>
      </div>
    );
  }
}
// 2. ----------------------------
class ChildB extends Component {
  render() {
    return (
      <div>
        <p>组件ChildB:</p>
        <ctx.Consumer>{value => <p>context:{value.a}</p>}</ctx.Consumer>
      </div>
    );
  }
}
// 函数组件
function ChildC() {
  return (
    <div>
      <p>组件ChildC:</p>
      <ctx.Consumer>{value => <p>context:{value.a}</p>}</ctx.Consumer>
    </div>
  );
}

注意细节

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

const ctx = React.createContext();

export default class NewContext extends Component {
  state = {
    a: 11
  };

  render() {
    return (
      <ctx.Provider value={this.state}>// state中的引用地址指向发生改变
        <button
          onClick={() => {
            this.setState({ });
          }}
        >
          父组件改变state值
        </button>
        <ChildA />
      </ctx.Provider>
    );
  }
}

class ChildA extends Component {
  static contextType = ctx;

  shouldComponentUpdate() {
    console.log("组件优化");  // 根本没有打印
    return false;
  }

  render() {
    console.log("渲染childA");  
    return (
      <div>
        <p>组件ChildA:</p>
        <p>context:{this.context.a}</p>
      </div>
    );
  }
}

// 优化
export default class NewContext extends Component {
  state = {
    ctx:{
        a:2
    }
  };

  render() {
    return (
      <ctx.Provider value={this.state.ctx}>// state中的引用地址指向发生改变
      .......
      </ctx.Provider>
    );
  }
}

发布了45 篇原创文章 · 获赞 14 · 访问量 3万+

猜你喜欢

转载自blog.csdn.net/cmchenmei/article/details/104009515