参考资料:javascript
这是牛津词典里有关 context 的解释。一般咱们在编程当中会把 context 叫作 上下文,不过我认为这里彷佛叫作 语境更合适一些。可是不论是叫作 上下文或是 语境,都仍是有些抽象。html
在学生时代,语文试卷中都会有个阅读理解部分,一般会有个问题相似于“结合上下文,x x x x x x x”,例如:java
..... 林冲大叫一声“啊也!” ..... 问:这句话林冲的“啊也”表达了林冲怎样的内心? 答:啊你妈个头啊!react
看,一篇文章,给你摘录一段,没前没后,你读不懂。由于有 语境,就是语言环境存在,一段话说了什么,要经过上下文(文章的上下文)来推断。git
那么从编程的角度又该如何理解呢?一般咱们写代码的时候,除了一些简单的函数不须要外部变量,其余的复杂些的函数大多会依赖一些外部变量。而一旦须要外部变量,那么这段代码就不是完整的,不是可以独立运行的,而这里这些程序运行所必需的外部变量(也能够理解为前置条件),就叫作 上下文。github
首先,react 在 16.3 引入了context,通过不过的迭代,到如今16.8.6的版本中, 官方对context的定义为 --- Context 提供了一个无需为每层组件手动添加 props,就能在组件树间进行数据传递的方法。归类于高级指引部分,属于react的高级api。web
一般在咱们写react组件的时候,数据传递通常采用单相数据流的方式,这样可使数据流向变得的简单而清晰。但在某些使用场景中须要共享一些对于一个组件树而言是**“全局”**的数据,这时就须要在每个层级手动修改传输的props,过于麻烦。 例如官方文档中提供的demo:编程
class App extends React.Component {
render() {
return <Toolbar theme="dark" />;
}
}
function Toolbar(props) {
// Toolbar 组件接受一个额外的“theme”属性,而后传递给 ThemedButton 组件。
// 若是应用中每个单独的按钮都须要知道 theme 的值,这会是件很麻烦的事,
// 由于必须将这个值层层传递全部组件。
return (
<div>
<ThemedButton theme={props.theme} />
</div>
);
}
class ThemedButton extends React.Component {
render() {
return <Button theme={this.props.theme} />;
}
}
复制代码
在上述代码中,修改Button的theme,那么须要从App中修改theme,传给Toolbar,并由Toolbar传给ThemedButton使用,如此层层传递。若是ThemedButton与App以前还存在更多的嵌套关系,那么必需将theme在中间的各层级间层层传递,显然,这样过于繁琐了。redux
// Context 可让咱们无须明确地传遍每个组件,就能将值深刻传递进组件树。
// 为当前的 theme 建立一个 context(“light”为默认值)。
const ThemeContext = React.createContext('light');
class App extends React.Component {
render() {
// 使用一个 Provider 来将当前的 theme 传递给如下的组件树。
// 不管多深,任何组件都能读取这个值。
// 在这个例子中,咱们将 “dark” 做为当前的值传递下去。
return (
<ThemeContext.Provider value="dark">
<Toolbar />
</ThemeContext.Provider>
);
}
}
// 中间的组件不再必指明往下传递 theme 了。
function Toolbar(props) {
return (
<div>
<ThemedButton />
</div>
);
}
class ThemedButton extends React.Component {
// 指定 contextType 读取当前的 theme context。
// React 会往上找到最近的 theme Provider,而后使用它的值。
// 在这个例子中,当前的 theme 值为 “dark”。
static contextType = ThemeContext;
render() {
return <Button theme={this.context} />;
}
}
复制代码
这是16.8.6版本中的文档demo(本文不对16.x以前的context使用方式作探讨,有兴趣同窗可自行研究)api
const ThemeContext = React.createContext('light');
复制代码
class App extends React.Component {
render() {
// 使用一个 Provider 来将当前的 theme 传递给如下的组件树。
// 不管多深,任何组件都能读取这个值。
// 在这个例子中,咱们将 “dark” 做为当前的值传递下去。
return (
<ThemeContext.Provider value="dark"> <Toolbar /> </ThemeContext.Provider> ); } } 复制代码
class ThemedButton extends React.Component {
// 指定 contextType 读取当前的 theme context。
// React 会往上找到最近的 theme Provider,而后使用它的值。
// 在这个例子中,当前的 theme 值为 “dark”。
static contextType = ThemeContext;
render() {
// 挂载在 class 上的 contextType 属性会被重赋值为一个由 React.createContext() 建立的 Context 对象。这能让你使用 this.context 来消费最近 Context 上的那个值。
return <Button theme={this.context} />; } } 复制代码
4.中间无需使用theme的组件,不须要层层传递props
// 中间的组件不再必指明往下传递 theme 了。
function Toolbar(props) {
return (
<div> <ThemedButton /> </div>
);
}
复制代码
class ThemedButton extends React.Component {
// 指定 contextType 读取当前的 theme context。
// React 会往上找到最近的 theme Provider,而后使用它的值。
// 在这个例子中,当前的 theme 值为 “dark”。
static contextType = ThemeContext;
render() {
return (
<ThemeContext.Consumer>
//Consumer容器,能够拿到上文传递下来的 theme 属性,并能够展现对应的值
{(theme) =>
<Button theme={theme} />
}
</ThemeContext.Consumer>
);
}
}
复制代码
1.由于每次Context值变动时,Consumer都会接受到相应的变化通知,这里可能会有一些陷阱,当 Provider 的父组件进行重渲染时,可能会在 Consumers 组件中触发意外的渲染。例如
class App extends React.Component {
render() {
//当每一次 Provider 重渲染时(即每次render时{something: 'something'}都指向一个新对象,拓展1),如下的代码会重渲染全部下面的 consumers 组件,由于 value 属性老是被赋值为新的对象
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>
);
}
}
复制代码
2.组件的复用性下降
1.引用类型 2.mobx-react / react-router / react-redux 均适用context,有兴趣可自行探究。