美文网首页
React Context

React Context

作者: 灭绝小师弟 | 来源:发表于2020-03-31 19:22 被阅读0次

    什么是context

    Context 提供了一个无需为每层组件手动添加 props,就能在组件树间进行数据传递的方法。
    在 React 中,数据是通过 props 属性自上而下进行传递的。但是当某些属性是许多组件都需要的(例如:地区偏好,UI 主题),这样就需要通过组件树的逐层传递 props,极其繁琐。Context 提供了一种在组件之间共享此类值的方式,使得不必再显式地逐层传递props。

    何时使用 Context

    当需要共享那些对于一个组件树而言是“全局”的数据,例如当前认证的用户、主题或首选语言。

    何时避免使用Context

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

    如果你只是想避免层层传递一些属性,组件组合有时候是一个比 context 更好的解决方案。

    React.createContext

    const MyContext = React.createContext(defaultValue);
    

    创建一个 Context 对象。当 React 渲染一个订阅了这个 Context 对象的组件,这个组件会从组件树中离自身最近的那个匹配的 Provider 中读取到当前的 context 值。只有当组件所处的树中没有匹配到 Provider 时,其 defaultValue 参数才会生效。

    Context.Provider

    <MyContext.Provider value={/* 某个值 */}>
    

    每个 Context 对象都会返回一个 Provider React 组件,它允许consumer组件订阅 context 的变化。

    • 一个 Provider 可以和多个消费组件有对应关系。多个 Provider 也可以嵌套使用,里层的会覆盖外层的数据。
    • 当 Provider 的 value 值发生变化时,它内部的所有消费组件都会重新渲染。Provider 及其内部 consumer 组件都不受制于 shouldComponentUpdate 函数,因此当 consumer 组件在其祖先组件退出更新的情况下也能更新。

    Class.contextType

    挂载在 class 上的 contextType 属性会被重赋值为一个由 React.createContext()创建的 Context 对象。这能让你使用 this.context 来消费最近 Context 上的那个值。你可以在任何生命周期中访问到它,包括 render 函数中。

    class MyClass extends React.Component {
      componentDidMount() {
        let value = this.context;
        /* 在组件挂载完成后,使用 MyContext 组件的值来执行一些有副作用的操作 */
      }
      componentDidUpdate() {
        let value = this.context;
        /* ... */
      }
      componentWillUnmount() {
        let value = this.context;
        /* ... */
      }
      render() {
        let value = this.context;
        /* 基于 MyContext 组件的值进行渲染 */
      }
    }
    MyClass.contextType = MyContext;
    

    或者使用 public class fields 语法,用 static 属性来初始化 contextType

    class MyClass extends React.Component {
      static contextType = MyContext;
      render() {
        let value = this.context;
        /* 基于这个值进行渲染工作 */
      }
    }
    

    Context.Consumer

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

    需要将函数作为子元素,这个函数接收当前的 context 值,返回一个 React 节点。传递给函数的 value 值等同于往上组件树离这个 context 最近的 Provider 提供的 value 值。如果没有对应的 Provider,value 参数等同于传递给 createContext()defaultValue

    Context.displayName

    context 对象接受一个名为 displayName 的属性,类型为字符串。React 开发者工具使用该字符串来确定 context 要显示的内容。

    const MyContext = React.createContext(/* some value */);
    MyContext.displayName = 'MyDisplayName';
    <MyContext.Provider> // "MyDisplayName.Provider" 在 开发工具 中
    <MyContext.Consumer> // "MyDisplayName.Consumer" 在 开发工具 中
    

    动态 Context

    Provider的value值,使用父组件中的state,state变化,context的value也随之变化。

    在嵌套组件中更新 Context

    从一个在组件树中嵌套很深的组件中更新 context 是很有必要的。在这种场景下,可以通过 context 传递一个函数,使得 consumers 组件可以更新 context。

    // 确保传递给 createContext 的默认值数据结构是调用的组件(consumers)所能匹配的!
    export const ThemeContext = React.createContext({
      theme: themes.dark,
      toggleTheme: () => {},
    });
    

    消费多个 Context

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

    // Theme context,默认的 theme 是 “light” 值
    const ThemeContext = React.createContext('light');
    
    // 用户登录 context
    const UserContext = React.createContext({
      name: 'Guest',
    });
    
    class App extends React.Component {
      render() {
        const {signedInUser, theme} = this.props;
    
        // 提供初始 context 值的 App 组件
        return (
          <ThemeContext.Provider value={theme}>
            <UserContext.Provider value={signedInUser}>
              <Layout />
            </UserContext.Provider>
          </ThemeContext.Provider>
        );
      }
    }
    
    function Layout() {
      return (
        <div>
          <Sidebar />
          <Content />
        </div>
      );
    }
    
    // 一个组件可能会消费多个 context
    function Content() {
      return (
        <ThemeContext.Consumer>
          {theme => (
            <UserContext.Consumer>
              {user => (
                <ProfilePage user={user} theme={theme} />
              )}
            </UserContext.Consumer>
          )}
        </ThemeContext.Consumer>
      );
    }
    

    注意事项

    如果context的value为一个对象,当每一次 Provider 重渲染时,会重渲染所有下面的 consumers 组件。原因是 value 属性总是被赋值为新的对象。

    // 触发意外的渲染
    class App extends React.Component {
      render() {
        return (
          <MyContext.Provider value={{something: 'something'}}>
            <Toolbar />
          </MyContext.Provider>
        );
      }
    }
    

    将 value 状态提升到父节点的 state 中,防止这种情况的出现

    class App extends React.Component {
      constructor(props) {
        super(props);
        this.state = {
          value: {something: 'something'},
        };
      }
    
      render() {
        return (
          <Provider value={this.state.value}>
            <Toolbar />
          </Provider>
        );
      }
    }
    

    相关文章

      网友评论

          本文标题:React Context

          本文链接:https://www.haomeiwen.com/subject/mjpbuhtx.html