React【 CreateContext 】

237 阅读2分钟

Introduction

Creates a Context object. When React renders a component that subscribes to this Context object it will read the current context value from the closest matching Provider above it in the tree.

Usage

step 1:

const MyContext = React.createContext(defaultValue);

step 2:

Parent component: 规定好通过value属性添加要传递给子组件的属性值

class Page extends React.Component{
    render () {
       const contextValue = { propOne: "some test here" };
       return (
        <MyContext.Provider value={ contextValue }>
            <Children />
        </MyContext.Provider>
       )
    }
}

step 3:

Child component: 返回一个函数式组件,并将传递过来的value属性作为参数传入

class Child extends React.Component{
    render () {
        {
            value => (<grandChild/>value.propOne</grandChild>)
        }
    }
}

Principle

method returns two class components: Provider & Consumer

function createContext (defaultValue) {
    class Provider extends React.Component {
        static value; 
        
        constructor(props) {
            Provider.value = props.value;
            this.state = { value: null };
        }
        
        getDerivedStateFromProps(nextProps, prevState) {
            Provider.value = nextProps.value;
            return { value: nextProps.value };
        }
        
        render () {
            return this.props.children;
        }
    }
    
    class Consumer extends React.Component {
        render () {
            return this.props.children(Provider.value);
        }
    }
    
    return {
        Provider,
        Consumer,
    }
}

这里给Provider添加类属性vaule,而不是给实例添加属性,是为了避免通过new来实例化组件才能拿到value值,节约性能。

栗子

import React from 'react';
import ReactDOM from 'react-dom';


// principle
function createContext() {
  class Provider extends React.Component {
    static value;   // { changeColor: this.changeColor, color: this.state.color };
    constructor(props) {
      super(props);
      Provider.value = props.value;
      this.state = { value: props.value }
    }
    static getDerivedStateFromProps(nextProps, prevState) {
      Provider.value = nextProps.value;
      return { value: nextProps.value };
    }
    render() {
      return this.props.children
    }
  }
  
  class Consumer extends React.Component {
    render() {
      return this.props.children(Provider.value);
    }
  }
  
  return {
    Provider,
    Consumer
  }
}

const ThemeContext = createContext();

class Page extends React.Component{
  constructor(props) {
    super(props);
    this.state = { color: '#d9534f', count: 0 };
  }

  changeColor = color => {
    this.setState({ color });
  };

  add = () => this.setState(state => ({ count: state.count + 2 }));

  minus = () => this.setState(state => (state.count ? { count: state.count -1 } : {count: state.count }));

  render () {
    let contextVal = { changeColor: this.changeColor, color: this.state.color, add: this.add, minus: this.minus };
    return (
      <ThemeContext.Provider value={ contextVal }>
        <div style={ { width: 300, padding: 5, margin: '50px auto', border: `4px solid ${this.state.color}` } }>
          <p> Count: { this.state.count }</p>
          <Header />
          <Main />
        </div>
      </ThemeContext.Provider>
    )
  }
}


class Header extends React.Component{
  render() {
    return (
      <ThemeContext.Consumer>
        {
          (value) => (
            <div style={{ border: `4px solid ${value.color}`, padding: 10 }}>
              Header
              <Title/>
            </div>
          )
        }
      </ThemeContext.Consumer>
    )
  }
}
class Title extends React.Component{
  render() {
    return (
      <ThemeContext.Consumer>
        {
          value => (
            <div style={{ border: `4px solid ${value.color}`, padding: 10 }}>Title</div>
          )
        }
      </ThemeContext.Consumer>
    )
  }
}
class Main extends React.Component{
  render() {
    return (
      <ThemeContext.Consumer>
        {
          value => (
            <div style={{ border: `4px solid ${value.color}`, padding: 10, marginTop: 10 }}>
              Main
              <Content />
            </div>
          )
        }
      </ThemeContext.Consumer>
    )
  }
}
class Content extends React.Component{
  render() {
    return (
      <ThemeContext.Consumer>
        {
          value => (
            <div style={{ border: `4px solid ${value.color}`, padding: 10 }}>
              Content
              <br/>
              <button className='btn btn-danger ml_10' onClick={() => value.changeColor("#d9534f")}>Pink</button>
              <button className='btn btn-info ml_10' onClick={() => value.changeColor("#46b8da")}>Green</button>
              <br/>
              <button className='btn btn-secondary ml_10' onClick={ value.minus }>-</button>
              <button className='btn btn-secondary ml_10' onClick={ value.add }>+</button>
            </div>
          )
        }
      </ThemeContext.Consumer>
    )
  }
}

ReactDOM.render(
    <Page />,
    document.getElementById('root')
);