Context 经过组件树提供了一个传递数据的方法,从而避免了在每个层级手动的传递 props 属性。算法
在一个典型的 React 应用中,数据是经过 props 属性由上向下(由父及子)的进行传递的,但这对于某些类型的属性而言是极其繁琐的(例如:地区偏好,UI主题),这是应用程序中许多组件都所须要的。 Context 提供了一种在组件之间共享此类值的方式,而没必要经过组件树的每一个层级显式地传递 props 。app
Context 设计目的是为共享那些被认为对于一个组件树而言是“全局”的数据,例如当前认证的用户、主题或首选语言。例如,在下面的代码中,咱们经过一个“theme”属性手动调整一个按钮组件的样式:ide
function ThemedButton(props) { return <Button theme={props.theme} />; } // 中间组件 function Toolbar(props) { // Toolbar 组件必须添加一个额外的 theme 属性 // 而后传递它给 ThemedButton 组件 return ( <div> <ThemedButton theme={props.theme} /> </div> ); } class App extends React.Component { render() { return <Toolbar theme="dark" />; } }
使用 context, 我能够避免经过中间元素传递 props:函数
// 建立一个 theme Context, 默认 theme 的值为 light const ThemeContext = React.createContext('light'); function ThemedButton(props) { // ThemedButton 组件从 context 接收 theme return ( <ThemeContext.Consumer> {theme => <Button {...props} theme={theme} />} </ThemeContext.Consumer> ); } // 中间组件 function Toolbar(props) { return ( <div> <ThemedButton /> </div> ); } class App extends React.Component { render() { return ( <ThemeContext.Provider value="dark"> <Toolbar /> </ThemeContext.Provider> ); } }
不要仅仅为了不在几个层级下的组件传递 props 而使用 context,它是被用于在多个层级的多个组件须要访问相同数据的情景测试
const {Provider, Consumer} = React.createContext(defaultValue);
建立一对 { Provider, Consumer }。当 React 渲染 context 组件 Consumer 时,它将从组件树的上层中最接近的匹配的 Provider 读取当前的 context 值。 若是上层的组件树没有一个匹配的 Provider,而此时你须要渲染一个 Consumer 组件,那么你能够用到 defaultValue 。这有助于在不封装它们的状况下对组件进行测试this
<Provider value={/* some value */}>
React 组件容许 Consumers 订阅 context 的改变。 接收一个 value 属性传递给 Provider 的后代 Consumers。一个 Provider 能够联系到多个 Consumers。Providers 能够被嵌套以覆盖组件树内更深层次的值。设计
<Consumer> {value => /* render something based on the context value */} </Consumer>
一个能够订阅 context 变化的 React 组件。 接收一个 函数做为子节点. 函数接收当前 context 的值并返回一个 React 节点。传递给函数的 value 将等于组件树中上层 context 的最近的 Provider 的 value 属性。若是 context 没有 Provider ,那么 value 参数将等于被传递给 createContext() 的 defaultValuecode
每当Provider的值发生改变时, 做为Provider后代的全部Consumers都会从新渲染。 从Provider到其后代的Consumers传播不受shouldComponentUpdate方法的约束,所以即便祖先组件退出更新时,后代Consumer也会被更新。 经过使用与Object.is相同的算法比较新值和旧值来肯定变化。io
主题的动态值,一个更加复杂的例子:function
theme-context.js
export const themes = { light: { foreground: '#ffffff', background: '#222222', }, dark: { foreground: '#000000', background: '#eeeeee', }, }; export const ThemeContext = React.createContext( themes.dark // 默认值 );
themed-button.js
import {ThemeContext} from './theme-context'; function ThemedButton(props) { return ( <ThemeContext.Consumer> {theme => ( <button {...props} style={{backgroundColor: theme.background}} /> )} </ThemeContext.Consumer> ); } export default ThemedButton;
app.js
import {ThemeContext, themes} from './theme-context'; import ThemedButton from './themed-button'; // 一个使用到ThemedButton组件的中间组件 function Toolbar(props) { return ( <ThemedButton onClick={props.changeTheme}> Change Theme </ThemedButton> ); } class App extends React.Component { constructor(props) { super(props); this.state = { theme: themes.light, }; this.toggleTheme = () => { this.setState(state => ({ theme: state.theme === themes.dark ? themes.light : themes.dark, })); }; } render() { // ThemedButton 位于 ThemeProvider 内 // 在外部使用时使用来自 state 里面的 theme // 默认 dark theme return ( <Page> <ThemeContext.Provider value={this.state.theme}> <Toolbar changeTheme={this.toggleTheme} /> </ThemeContext.Provider> <Section> <ThemedButton /> </Section> </Page> ); } } ReactDOM.render(<App />, document.root);