开发者社区> 好程序员> 正文
阿里云
为了无法计算的价值
打开APP
阿里云APP内打开

Context-React跨组件访问数据的利器

简介:   Context-React跨组件访问数据的利器  Context提供了一种跨组件访问数据的方法。它无需在组件树间逐层传递属性,也可以方便的访问其他组件的数据  在经典的React应用中,数据是父组件通过props向子组件传递的。
+关注继续查看

  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
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

{value => / render something based on the context value /}
.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
      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(, 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>
);

}
}

版权声明:本文内容由阿里云实名注册用户自发贡献,版权归原作者所有,阿里云开发者社区不拥有其著作权,亦不承担相应法律责任。具体规则请查看《阿里云开发者社区用户服务协议》和《阿里云开发者社区知识产权保护指引》。如果您发现本社区中有涉嫌抄袭的内容,填写侵权投诉表单进行举报,一经查实,本社区将立刻删除涉嫌侵权内容。

相关文章
React Native 数据存储
1. AsyncStorage 1). AsyncStorage AsyncStorage是一个简单的、异步的、持久化的Key-Value存储系统,它对于App来说是全局性的。
1083 0
React Native之导出
React Native之导出
49 0
React Native之提示Unable to load script from assets ‘index.android.bundle
React Native之提示Unable to load script from assets ‘index.android.bundle
32 0
react native 开发常用优质第三方组件
轻提示 react-native-root-toast git 链接:https://github.com/magicismight/react-native-root-toast 获取设备信息 react-native-device-info git 链接:https://github.
1596 0
《React Native 精解与实战》书籍连载「React Native 中的生命周期」
此文是我的出版书籍《React Native 精解与实战》连载分享,此书由机械工业出版社出版,书中详解了 React Native 框架底层原理、React Native 组件布局、组件与 API 的介绍与代码实战,以及 React Native 与 iOS、Android 平台的混合开发底层原理讲解与代码实战演示,精选了大量实例代码,方便读者快速学习。
1266 0
《React Native 精解与实战》书籍连载「React Native 底层原理」
此文是我的出版书籍《React Native 精解与实战》连载分享,此书由机械工业出版社出版,书中详解了 React Native 框架底层原理、React Native 组件布局、组件与 API 的介绍与代码实战,以及 React Native 与 iOS、Android 平台的混合开发底层原理讲解与代码实战演示,精选了大量实例代码,方便读者快速学习。
1558 0
React Native之hellWord
初始化项目工程 进入自己的工作空间然后shift+鼠标右键打开命令行窗口执行如下命令创建RN工程HelloWorld: 然后使用Android Studio打开AVD Manager创建模拟器,在打开Android Studio的时候会提示创建Android项目,我们的目的只是为了打开模拟器,就随便创建一个项目就行了,创建之后就直接打开模拟器:
1348 0
React Native之样式
样式 React Native 不实现 CSS,而是依赖于 JavaScript 来为你的应用程序设置样式。这是一个有争议的决定,你可以阅读那些幻灯片,了解背后的基本原理。 声明样式 在 React Native 中声明样式的方法如下: var styles = StyleSheet.create({ base: { width: 38, hei
1725 0
+关注
333
文章
0
问答
文章排行榜
最热
最新
相关电子书
更多
低代码开发师(初级)实战教程
立即下载
阿里巴巴DevOps 最佳实践手册
立即下载
冬季实战营第三期:MySQL数据库进阶实战
立即下载