React 高级指引:Context

1,156 阅读2分钟

前言

Context 提供了一个无需未每层组件手动添加 props,就能在组件树之间进行数据传递的方法。

在一个典型的 React 应用中,数据是通过 props 属性自上而下(从父组件到子组件)进行传递的,但是这样的方式对于某些类型的属性显得十分繁琐(例如地区偏好,UI 主题),这些属性是应用程序中许多组件都需要的,Context 提供了一个在组件之间共享此类值的方式,而不必显式地通过组件树逐层传递 props 。

场景

Context 设计的目的是为了共享那些对于组件树而言是全局的数据,例如当前认证的用户、主题或者语言。

下面的例子,通过一个 theme 属性手动调整一个按钮组件的样式:

class App extends React.component {
  render(){
    return <Toolbar theme='dark'></Toolbar>;
  }
}

// Toolbar 组件接受一个额外的 theme 属性,传递给 ThemeButton 组件
// 如果应用中每一个单独的组件都需要获得 theme 的值,就需要层层传递到所有组件
function Toolbar(props){
  return (
    <div>
      <ThemeButton theme={props.theme} ></ThemeButton>
    </div>
  )
}

function ThemeButton(props){
  return <Button theme={props.theme}></Button>
}

使用 Context 就可以避免层层传递:

// 为 theme 创建一个 context
const ThemeContext = React.createContext('light');
class App extends React.Component{
  render(){
    // 使用一个 provider 将当前的 theme 传递给下面的组件树
    return (
      <ThemeContext.Provider value='dark'>
        <Toolbar></Toolbar>
      </ThemeContext.Provider>
    );
  }
}

// 中间组件不需要传递 theme
function Toolbar(){
  return (
    <div>
      <ThemeButton></ThemeButton>
    </div>
  );
}

class ThemeButton extends React.Component{
  // React 会从当前组件顺着组件树往上找到最近的 Provider 使用它的值,没找到就使用默认值
  static contextType = ThemeContext;
  render(){
    return <Button theme={this.context}></Button>;
  }
}

使用方法

创建 Context

使用 React.createContext 来创建,生成的 Context 对象的数据结构:

  • 两个 currentValue (用来支持多个渲染器)
  • Provider
    • 包含指向当前 Context 的引用
  • Consumer
    • 包含指向当前 Context 的引用
  • 当前组件向上没有匹配到 Provider 的时候使用 createContext 的默认值(用来测试)

声明 Context 作用域

使用 Context.Provider 来声明使用 Context 的作用域,对应的节点会使用 updateContextProvider 来使用 value 更新 Context

一般都是通过修改 Provider 的 value 的状态来更新 Context

组件中使用

React 提供三种方法来使用 Context 分别对应不同的场景:

  • Consumer 用于 JSX
  • contextType 用于 class 组件
    • 多个 context 好像没法使用该方法
  • useContext 用于函数组件

内部都会调用 readContext 来获取最近 Provider 提供的 context 最新值

使用 context 的组件会随着 context 修改而重新渲染