ITPub博客

首页 > 应用开发 > IT综合 > 好程序员web前端分享Context-React跨组件访问数据的利器

好程序员web前端分享Context-React跨组件访问数据的利器

原创 IT综合 作者:好程序员IT 时间:2019-11-13 17:04:11 0 删除 编辑

  好程序员web 前端分享 Context-React 跨组件访问数据的利器

  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值

如果编码中使用了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的默认值)。

注意

更多关于“子元素为一个函数”的信息

栗子

在嵌套组件中更新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

          >

          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 themeconst  ThemeContext = React.createContext( 'light' );

// Signed-in user contextconst  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>

    );

  }

}


 


来自 “ ITPUB博客 ” ,链接:https://blog.itpub.net/69913892/viewspace-2663962/ ,如需转载,请注明出处,否则将追究法律责任。

请登录后发表评论 登录
全部评论
欢迎关注公众号:好程序员特训营 web前端教程分享 723729549 大数据教程分享 703503210

注册时间:2019-03-20

  • 博文量
    539
  • 访问量
    410752