React.js |Context的作用与用法 Context是什么? API 示例01-动态Context 示例02-在嵌套组件中更新 Context 示例03-消费多个 Context

参照react官方文档,文章用于笔记整理。

在react应用中,数据总是通过 props 自上而下进行传递。 这种做法对于某些类型的属性而言是极其繁琐的(例如:地区偏好,UI 主题)。Context 可以共享对于一个组件树而言是“全局”的数据。这样就不必显式地通过组件树的逐层传递 props

class App extends React.Component {
  render() {
    return <Toolbar theme="dark" />;
  }
}
//传递prop
function Toolbar(props) {
  return (
    <div>
      <ThemedButton theme={props.theme} />
    </div>
  );
}
//再传递prop
class ThemedButton extends React.Component {
  render() {
    return <Button theme={this.props.theme} />;
  }
}

使用context可以避免通过中间元素传递 props

//1.为 theme 创建一个 context,默认值为light
const ThemeContext = React.createContext('light');

class App extends React.Component {
  render() {
    return (
      // 2.使用 Provider 传递theme。在这里,将 “dark” 传递下去
      <ThemeContext.Provider value="dark">
        <Toolbar />
      </ThemeContext.Provider>
    );
  }
}

// !中间组件不必指明
function Toolbar() {
  return (
    <div>
      <ThemedButton />
    </div>
  );
}

class ThemedButton extends React.Component {
  // 3.指定 contextType 读取 theme的context对象。
  static contextType = ThemeContext;
  // 4.React 会往上找到最近的 Provider,然后通过this.context读取Provider的value值。
  render() {
    return <Button theme={this.context} />;
  }
}

Context 主要应用场景在于很多不同层级的组件需要访问同样一些的数据。谨慎使用,因为这会使得组件的复用性变差

API

React.createContext

创建一个Context对象:

const MyContext = React.createContext(defaultValue);

Context.Provider

Context 对象会返回的一个Provider React 组件:

<MyContext.Provider value={某个值}/>
  • Provider 接收一个 value 属性,传递给消费组件(provider React 组件内的组件)
  • Provider 的 value 值发生变化时,它内部的所有消费组件都会重新渲染
  • 当消费组件没有匹配到 Provider , Context对象的defaultValue 参数生效(注意:将 undefined 传递给 Provider 的 value 时,defaultValue 不会生效)
  • Provider 可以和多个消费组件有对应关系。多个 Provider 也可以嵌套使用,里层的会覆盖外层的数据

Class.contextType

如果使用public class fields 语法,可以用 static 初始化contextType。contextType指定了需要读取的Context对象

class MyClass extends React.Component {
  static contextType = MyContext;
  render() {
    let value = this.context;
  }
}

更常用的是Class.contextTypeClass.contextType可以对Context对象重赋值:

class MyClass extends React.Component {
 //...
  render() {
    let value = this.context;
  }
}
//重赋值
MyClass.contextType = MyContext;

挂载在 class 的 contextType 会被重赋值为一个由 React.createContext() 创建的 Context 对象。使你用 this.context 来访问最近 Context 值。可以在任何生命周期中访问到它

Context.Consumer

基于 context 值渲染React节点

<MyContext.Consumer>
  {value => /* 基于 context 值进行渲染*/}
</MyContext.Consumer>

这需要函数作为子元素(function as a child)这种做法。函数接收 context 值,返回一个 React 节点。传递给函数的 value 值等于离这个 context 最近的 Provider 提供的 value 值。

Context.displayName

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

const MyContext = React.createContext(/* some value */);
MyContext.displayName = 'MyDisplayName';

<MyContext.Provider> // 在 DevTools 中会显示"MyDisplayName.Provider" 

示例01-动态Context

theme-context.js:储存themes对象和theme的context对象

//1. 声明一个themes对象储存主题 导出
export const themes = {
    light: {
      background: 'orange',
    },
    dark: {
      background: '#eee',
    },
  };
  
//2. 创建context对象 导出
export const ThemeContext = React.createContext(
    themes.dark     //2.设置默认值dark
);

themed-button.js:创建一个加了主题的按钮组件(ThemedButton)

//1. 引入theme的context对象
import {ThemeContext} from './theme-context';

class ThemedButton extends Component {
  render() {
    let props = this.props;
    return (
      <button
        {...props}
        style={{backgroundColor: this.context.background}} //3. 读取值
      />
    );
  }
}

//2. context重赋值
ThemedButton.contextType = ThemeContext;

//4. 导出按钮组件
export default ThemedButton;

App.js

//1.引入context对象和themes对象
import {ThemeContext, themes} from './theme-context';
//2.引入按钮组件
import ThemedButton from './themed-button';

// 中间组件 放入按钮,用于切换主题
function Toolbar(props) {
  return (
    <ThemedButton onClick={props.changeTheme}>
      Change Theme
    </ThemedButton>
  );
}

//父组件
class App extends Component {
  constructor(props) {
    super(props);
    this.state = {
      theme: themes.light,
    };

    //3.定义切换主题的事件
    this.toggleTheme = () => {
      this.setState(state => ({
        theme:state.theme === themes.dark? themes.light:themes.dark,
      }));
    };
  }

  render() {
    return (
      <div>
        {/* 4.给Context对象的Provider的value赋值*/}
        <ThemeContext.Provider value={this.state.theme}>
          {/* 5.prop赋值 传入事件*/}
          <Toolbar changeTheme={this.toggleTheme} />
        </ThemeContext.Provider>

        {/* 6.用于对比,由于没有Provider提供value值,所以会读取context对象的默认值为dark */}
        <div>
          <Toolbar />
        </div>
      </div>
    );
  }
}
export default App

示例02-在嵌套组件中更新 Context

通过 context 传递一个函数,使得消费组件自行更新 context(场景:在嵌套很深的组件中更新 context ):

theme-context.js

export const ThemeContext = React.createContext({
  theme: themes.dark,
  toggleTheme: () => {},
});

theme-toggler-button.js

import {ThemeContext} from './theme-context';

// 这个按钮组件不仅获取了 theme 值,也从 context 中获取到一个 toggleTheme 函数
function ThemeTogglerButton() {
  return (
    //Context.Consumer:基于 context 值渲染React节点
    <ThemeContext.Consumer>
      {({theme, toggleTheme}) => (
        <button onClick={toggleTheme}
          style={{backgroundColor: theme.background}}>
          Toggle Theme
        </button>
      )}
    </ThemeContext.Consumer>
  );
}
export default ThemeTogglerButton;

App.js

import {ThemeContext, themes} from './theme-context';
import ThemeTogglerButton from './theme-toggler-button';

class App extends Component {
  constructor(props) {
    super(props);
    //1.定义事件
    this.toggleTheme = () => {
      this.setState(state => ({
        theme:state.theme === themes.dark? themes.light:themes.dark,
      }));
    };
    //2。定义state
    this.state = {
        theme: themes.light,
        toggleTheme: this.toggleTheme,
      };
  }
  render() {
    // 3.给Provider的value赋值,包含了当前的主题和事件
    return (
      <ThemeContext.Provider value={this.state}>
        <ThemeTogglerButton />
      </ThemeContext.Provider>
    );
  }
}

示例03-消费多个 Context

为了确保 context 快速进行重渲染,React 需要使每一个 消费组件的 context 在组件树中成为一个单独的节点。

App.js

import ProfilePage from './ProfilePage'

// 1.创建主题context
const ThemeContext = React.createContext({
  bgColor:'#eee',
  fontColor:'balck'
});
// 2.创建用户context
const UserContext = React.createContext({
  name: 'Guest',
});


class App extends Component {
  constructor(props){
    super(props)
    //3.设置在app组件的想要的状态
    this.state = {
      theme: {
        bgColor:'orange',
        fontColor:'white'
      },
      user: {
        name:'Jack'
      },
    };
  }
    
  render() {
    const {theme,user}=this.state
    //4.给Provider的value传入值
    return (
      <ThemeContext.Provider value={theme}>
        <UserContext.Provider value={user}>
            <Content />
        </UserContext.Provider>
      </ThemeContext.Provider>
    );
  }
}

function Content() {
  //5.现在可以访问到Provider传过来的theme和user
  return (
    <ThemeContext.Consumer>
      {theme => (
        <UserContext.Consumer>
          {user => (
            <ProfilePage user={user} theme={theme} />
          )}
        </UserContext.Consumer>
      )}
    </ThemeContext.Consumer>
  );
}

export default App

ProfilePage.js

export default function ProfilePage (props){
    const {user,theme}=props
    return(
        //给style传入了一个对象
        <div style={{display:'inline-block',padding:'5px 8px',backgroundColor:theme.bgColor,color:theme.fontColor}}>
            {user.name}
        </div>
    )
}

可在分支10、11、12中获取示例源码