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

Context - React跨组件访问数据的利器的更多相关文章

  1. Vue用axios跨域访问数据

    Vue用axios跨域访问数据axios是vue-resource的替代品,vue-resource不再维护.安装axios:npm install axios使用vue-cli开发时,由于项目本身启 ...

  2. 设置IE浏览器跨域访问数据

    在开发中,经常会遇到多站点跨域访问后台服务获取数据的情况,解决方法有两种 自己写代理服务,访问代理服务,代理服务请求服务获取数据再返回: 设置浏览器可以跨域访问数据. 本文来讲如何设置IE浏览器跨域访 ...

  3. C:static 关键字、静态变量、跨类访问数据

    static 在OC中的使用 参考1   参考2   参考3   参保4    参考5 跨类访问成员 参考 +号方法就是类方法(静态方法),说明不用创建对象,可以直接通过类型去直接调用这个方法,在OC ...

  4. React中的Context——从父组件传递数据

    简介:在React中,数据可以以流的形式自上而下的传递,每当你使用一个组件的时候,你可以看到组件的props属性会自上而下的传递.但是,在某些情况下,我们不想通过父组件的props属性一级一级的往下传 ...

  5. 通过FEDERATED存储引擎跨实例访问数据

    通过FEDERATED存储引擎同步两实例间的表数据需求情景:实例1中A库中的三个视图是实例2中的B库所依赖的,B需要A库中三个视图的实时数据.方案:通过FEDERATED来完成跨势力的查询FEDERA ...

  6. 在Vue中如何使用axios跨域访问数据

    最近在项目中需要用到axios,所以就恶补一下这个axios到底是什么东东.越来它是vue-resource的替代品,官网也说了,以后都用axios, vue-resource不在维护.那么这个axi ...

  7. 在Vue中如何使用axios跨域访问数据(转)

    最近在项目中需要用到axios,所以就恶补一下这个axios到底是什么东东.越来它是vue-resource的替代品,官网也说了,以后都用axios, vue-resource不在维护.那么这个axi ...

  8. Js C# 实现跨域访问数据

    使用项目一的js调用项目二的数据 1.项目一 @{ Layout = null; } <!DOCTYPE html> <html> <head> <meta ...

  9. android——实现跨程序访问数据

    使用之前的SQLite存储的应用程序.首先需要在这个应用程序中创建内容提供器,右击com.example.administrator.exp7包→New→Other→Content Provider, ...

随机推荐

  1. ZOJ 3209

    精确覆盖 #include <iostream> #include <cstdio> #include <cstring> #include <algorit ...

  2. ScalaChina: Scala中文社区

    给大家推荐一个很常使用心的Scala中文社区 ScalaChina地址:http://scalachina.org/ 来自社区创建者的<我为什么想做ScalaChina>: http:// ...

  3. OLR文件丢失的恢复

    11.2.0.1的RAC中,rac1和rac2 一.OLR有备份的情况 1.手动将rac1中的olr重命名,模拟丢失 mv rac1.olr rac1.olr.test 2.重新启动crs ./crs ...

  4. JavaScript 和Ajax跨域问题

    json格式: { "message":"获取成功", "state":"1", "result": ...

  5. AES 加密位: 128位,加密模式:CBC, 填充模式:Zeros

    // AES 加密 public byte[] AESEncrypt(string text) { byte[] data = Encoding.Unicode.GetBytes(text); Sym ...

  6. 2016.04.19,英语,《Vocabulary Builder》Unit 16

    top, comes from topos, the Greek word for 'place'. ectopic: [ek'tɑːpɪk] adj. [医]异位的,异常的 topical: ['t ...

  7. hdoj--1257--最少拦截系统(动态规划)

    最少拦截系统 Time Limit: 1000MS   Memory Limit: 32768KB   64bit IO Format: %I64d & %I64u Submit Status ...

  8. 南海区行政审批管理系统接口规范v0.3(规划) 5.投资项目联合审批系统API 5.1.【uploadFile】证件文书附件上传

  9. Serializable-源码分析

    package java.io; public interface Serializable { } 代码很简单,功能也很简单,对象通过这个接口来实现序列化和反序列的.下面来看看小例子. import ...

  10. 利用ajax,canvas实现的测试php程序占用内存的代码

    receive.php <?php $array["time"]=time();$array["memory"]=memory_get_usage();e ...