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