zoukankan      html  css  js  c++  java
  • Context

    Context提供了一种跨组件访问数据的方法。它无需在组件树间逐层传递属性,也可以方便的访问其他组件的数据

    在经典的React应用中,数据是父组件通过props向子组件传递的。但是在某些特定场合,有些数据需要在各个组件之间共享。 Context 为我们提供一种组件之间共享数据的方式,可以避免数据在组件树上逐层传递

    使用Context的场合

    Context可以在组件树的组件之间共享“全局”数据。例如:登陆的用户信息,用户选择的主题、语言等等。下面的例子中,我们“手动”自上而下传递theme属性,用来设定Button的样式。

    class App extends React.Component {
      render() {
        return <Toolbar theme="dark"></Toolbar>;
      }
    }
    
    function Toolbar(props) {
      // The Toolbar component must take an extra "theme" prop
      // and pass it to the ThemedButton. This can become painful
      // if every single button in the app needs to know the theme
      // because it would have to be passed through all components.
      return (
        <div>
          <ThemedButton theme={props.theme}></ThemedButton>
        </div>
      );
    }
    
    class ThemedButton extends React.Component {
      render() {
        return <Button theme={this.props.theme}></Button>;
      }
    }
    

    使用 Context ,我们可以避免通过多个中间组件传递props

    // Context lets us pass a value deep into the component tree
    // without explicitly threading it through every component.
    // Create a context for the current theme (with "light" as the default).
    const ThemeContext = React.createContext('light');
    
    class App extends React.Component {
      render() {
        // Use a Provider to pass the current theme to the tree below.
        // Any component can read it, no matter how deep it is.
        // In this example, we're passing "dark" as the current value.
        return (
          <ThemeContext.Provider value="dark">
            <Toolbar></Toolbar>
          </ThemeContext.Provider>
        );
      }
    }
    
    // A component in the middle doesn't have to
    // pass the theme down explicitly anymore.
    function Toolbar(props) {
      return (
        <div>
          <ThemedButton />
        </div>
      );
    }
    
    class ThemedButton extends React.Component {
      // Assign a contextType to read the current theme context.
      // React will find the closest theme Provider above and use its value.
      // In this example, the current theme is "dark".
      static contextType = ThemeContext;
      render() {
        return <Button theme={this.context} />;
      }
    }
    

    有时候,有些数据需要被很多组件访问,而且这些组件在组件树的不同层上。 Context 可以使我们以“广播”的形式,在各个组件中共享数据的改变

    Context相关API

    React.createContext

    const MyContext = React.createContext(defaultValue);
    

    创建一个新的 Context 对象。当React渲染一个组件,且该组件注册了 Context 时,它将读取父组件中,距离该组件最近的Provider组件的 Context

    defaultValue 只有 在“Consumer”组件找不到Provider组件时,才会被使用。

    Context.Provider

    <MyContext.Provider value={/* some value */}>
    

    每个 Context 对象都携带一个名叫Provider的React组件。Provider可以使得“Consumer”组件监听context的变更

    通过向Provider的后代Consumer组件传递value的prop,一个Provider可以与多个Consumer组件建立联系。

    所有的后代Consumer组件在Provider的value属性更新后,都会被重新渲染。这个更新从Provider到其后代Consumer组件之间传播,但是并不会触发shouldComponentUpdate方法。所以即使Consumer组件的祖先组件没有更新,Consumer组件也会更新

    Context使用与Object.is相同的算法来对比value的新、旧值,以判定其value是否被更新了

    注意

    当向value传递对象时,这种判定value是否改变的方式可能会引起问题。请参加.

    Class.contextType

    class MyClass extends React.Component {
      componentDidMount() {
        let value = this.context;
        /* perform a side-effect at mount using the value of MyContext */
      }
      componentDidUpdate() {
        let value = this.context;
        /* ... */
      }
      componentWillUnmount() {
        let value = this.context;
        /* ... */
      }
      render() {
        let value = this.context;
        /* render something based on the value of MyContext */
      }
    }
    MyClass.contextType = MyContext;
    

    为class的contextTpe属性赋值一个 Context 对象后,我们可以通过this.context在组件的各个声明周期函数中获取到当前的 Context 对象的方法

    注意:

    通过这种方式,每个组件只能注册一个context对象。如果需要读取多个context的value值,参加Consuming Multiple Contexts.

    如果编码中使用了ES实验中的语法,那么可以使用类的静态(static)成员来初始化contextTYpe.代码如下:

    class MyClass extends React.Component {
     static contextType = MyContext;
     render() {
       let value = this.context;
       /* render something based on the value */
     }
    }
    

    Context.Consumer

    <MyContext.Consumer>
      {value => /* render something based on the context value */}
    </MyContext.Consumer>
    

    Consumer是一个监听context变化的React组件。它使得我们可以在一个函数组件中,监听contxt的改变。

    Consumer组件要求其子元素为一个函数。该函数的参数接收当前的context的value值,要求返回一个React节点(node) 传递给该函数的参数value等于距离此 Consumner 最近的外层Provider组件的context值。如果没有外层的Provider组件,则等于调用createContext()时传递的参数值(context的默认值)。

    注意

    更多关于“子元素为一个函数”的信息,请参加render props

    栗子

    在嵌套组件中更新Context

    开发中,我们经常需要在某些嵌套结构很深的组件上更新context的value值。此时,我们可以向下传递一个函数,用它来更新context的value。代码如下:

    theme-context.js

    // Make sure the shape of the default value passed to
    // createContext matches the shape that the consumers expect!
    export const ThemeContext = React.createContext({
      theme: themes.dark,
      toggleTheme: () => {},
    });
    

    theme-toggler-button.js

    import {ThemeContext} from './theme-context';
    
    function ThemeTogglerButton() {
      // The Theme Toggler Button receives not only the theme
      // but also a toggleTheme function from the context
      return (
        <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 React.Component {
      constructor(props) {
        super(props);
    
        this.toggleTheme = () => {
          this.setState(state => ({
            theme:
              state.theme === themes.dark
                ? themes.light
                : themes.dark,
          }));
        };
    
        // State also contains the updater function so it will
        // be passed down into the context provider
        this.state = {
          theme: themes.light,
          toggleTheme: this.toggleTheme,
        };
      }
    
      render() {
        // The entire state is passed to the provider
        return (
          <ThemeContext.Provider value={this.state}>
            <Content />
          </ThemeContext.Provider>
        );
      }
    }
    
    function Content() {
      return (
        <div>
          <ThemeTogglerButton />
        </div>
      );
    }
    
    ReactDOM.render(<App />, document.root);
    

    使用多个Contexts

    为了保持React的快速渲染,我们需要将每个consumer组件编写成一个独立的组件节点(node)

    // Theme context, default to light theme
    const ThemeContext = React.createContext('light');
    
    // Signed-in user context
    const UserContext = React.createContext({
      name: 'Guest',
    });
    
    class App extends React.Component {
      render() {
        const {signedInUser, theme} = this.props;
    
        // App component that provides initial context values
        return (
          <ThemeContext.Provider value={theme}>
            <UserContext.Provider value={signedInUser}>
              <Layout />
            </UserContext.Provider>
          </ThemeContext.Provider>
        );
      }
    }
    
    function Layout() {
      return (
        <div>
          <Sidebar />
          <Content />
        </div>
      );
    }
    
    // A component may consume multiple contexts
    function Content() {
      return (
        <ThemeContext.Consumer>
          {theme => (
            <UserContext.Consumer>
              {user => (
                <ProfilePage user={user} theme={theme} />
              )}
            </UserContext.Consumer>
          )}
        </ThemeContext.Consumer>
      );
    }
    

    如果有两个以上的context经常一起使用,我们需要考虑创建一个render prop component一并提供两个Context

    注意

    因为context使用引用标示符(reference identity)来判断何时需要重新渲染,所以有些情况下,当provider的父元素重新渲染时,会触发consumer的非内部渲染。例如下面代码,在每次Provider重新渲染时,会重新渲染所有的consumer组件。因为会一直创建一个新的对象赋值给value(value一直在变)

    class App extends React.Component {
      render() {
        return (
          <Provider value={{something: 'something'}}>
            <Toolbar />
          </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>
        );
      }
    }
    

    原文地址:https://segmentfault.com/a/1190000016941968

  • 相关阅读:
    华为的管理变革之路
    产品创新型组织变革的四个阶段
    新产品如何在市场上快速取得成功?
    如何做好基础技术的创新?
    产品创新型总经理应具备哪些方面的素质?
    项目型组织如何快速过渡到产品型组织?
    华为是如何做技术规划和产品路标开发的?
    华为干部选拔和任用的标准
    导论:1、大学计算机——2、计算机信息数字化基础——二进制&数字化(数制)
    导论:1、大学计算机——1、计算机与问题求解
  • 原文地址:https://www.cnblogs.com/lalalagq/p/9940568.html
Copyright © 2011-2022 走看看