參照react官方文檔,文章用於筆記整理。
Context是什么?
在react應用中,數據總是通過 props 自上而下進行傳遞。 這種做法對於某些類型的屬性而言是極其繁瑣的(例如:地區偏好,UI 主題)。Context 可以共享對於一個組件樹而言是“全局”的數據。這樣就不必顯式地通過組件樹的逐層傳遞 props
class App extends React.Component {
render() {
return <Toolbar theme="dark" />;
}
}
//傳遞prop
function Toolbar(props) {
return (
<div>
<ThemedButton theme={props.theme} />
</div>
);
}
//再傳遞prop
class ThemedButton extends React.Component {
render() {
return <Button theme={this.props.theme} />;
}
}
使用context可以避免通過中間元素傳遞 props
//1.為 theme 創建一個 context,默認值為light
const ThemeContext = React.createContext('light');
class App extends React.Component {
render() {
return (
// 2.使用 Provider 傳遞theme。在這里,將 “dark” 傳遞下去
<ThemeContext.Provider value="dark">
<Toolbar />
</ThemeContext.Provider>
);
}
}
// !中間組件不必指明
function Toolbar() {
return (
<div>
<ThemedButton />
</div>
);
}
class ThemedButton extends React.Component {
// 3.指定 contextType 讀取 theme的context對象。
static contextType = ThemeContext;
// 4.React 會往上找到最近的 Provider,然后通過this.context讀取Provider的value值。
render() {
return <Button theme={this.context} />;
}
}
Context 主要應用場景在於很多不同層級的組件需要訪問同樣一些的數據。謹慎使用,因為這會使得組件的復用性變差
API
React.createContext
創建一個Context對象:
const MyContext = React.createContext(defaultValue);
Context.Provider
Context 對象會返回的一個Provider React 組件:
<MyContext.Provider value={某個值}/>
- Provider 接收一個 value 屬性,傳遞給消費組件(provider React 組件內的組件)
- Provider 的 value 值發生變化時,它內部的所有消費組件都會重新渲染
- 當消費組件沒有匹配到 Provider , Context對象的defaultValue 參數生效(注意:將 undefined 傳遞給 Provider 的 value 時,defaultValue 不會生效)
- Provider 可以和多個消費組件有對應關系。多個 Provider 也可以嵌套使用,里層的會覆蓋外層的數據
Class.contextType
如果使用public class fields 語法,可以用 static 初始化contextType。contextType指定了需要讀取的Context對象:
class MyClass extends React.Component {
static contextType = MyContext;
render() {
let value = this.context;
}
}
更常用的是Class.contextType。Class.contextType可以對Context對象重賦值:
class MyClass extends React.Component {
//...
render() {
let value = this.context;
}
}
//重賦值
MyClass.contextType = MyContext;
掛載在 class 的 contextType 會被重賦值為一個由 React.createContext() 創建的 Context 對象。使你用 this.context 來訪問最近 Context 值。可以在任何生命周期中訪問到它
Context.Consumer
基於 context 值渲染React節點
<MyContext.Consumer>
{value => /* 基於 context 值進行渲染*/}
</MyContext.Consumer>
這需要函數作為子元素(function as a child)這種做法。函數接收 context 值,返回一個 React 節點。傳遞給函數的 value 值等於離這個 context 最近的 Provider 提供的 value 值。
Context.displayName
在React DevTools 中對顯示的context對象的名稱進行更改
const MyContext = React.createContext(/* some value */);
MyContext.displayName = 'MyDisplayName';
<MyContext.Provider> // 在 DevTools 中會顯示"MyDisplayName.Provider"
示例01-動態Context
theme-context.js:儲存themes對象和theme的context對象
//1. 聲明一個themes對象儲存主題 導出
export const themes = {
light: {
background: 'orange',
},
dark: {
background: '#eee',
},
};
//2. 創建context對象 導出
export const ThemeContext = React.createContext(
themes.dark //2.設置默認值dark
);
themed-button.js:創建一個加了主題的按鈕組件(ThemedButton)
//1. 引入theme的context對象
import {ThemeContext} from './theme-context';
class ThemedButton extends Component {
render() {
let props = this.props;
return (
<button
{...props}
style={{backgroundColor: this.context.background}} //3. 讀取值
/>
);
}
}
//2. context重賦值
ThemedButton.contextType = ThemeContext;
//4. 導出按鈕組件
export default ThemedButton;
App.js
//1.引入context對象和themes對象
import {ThemeContext, themes} from './theme-context';
//2.引入按鈕組件
import ThemedButton from './themed-button';
// 中間組件 放入按鈕,用於切換主題
function Toolbar(props) {
return (
<ThemedButton onClick={props.changeTheme}>
Change Theme
</ThemedButton>
);
}
//父組件
class App extends Component {
constructor(props) {
super(props);
this.state = {
theme: themes.light,
};
//3.定義切換主題的事件
this.toggleTheme = () => {
this.setState(state => ({
theme:state.theme === themes.dark? themes.light:themes.dark,
}));
};
}
render() {
return (
<div>
{/* 4.給Context對象的Provider的value賦值*/}
<ThemeContext.Provider value={this.state.theme}>
{/* 5.prop賦值 傳入事件*/}
<Toolbar changeTheme={this.toggleTheme} />
</ThemeContext.Provider>
{/* 6.用於對比,由於沒有Provider提供value值,所以會讀取context對象的默認值為dark */}
<div>
<Toolbar />
</div>
</div>
);
}
}
export default App
示例02-在嵌套組件中更新 Context
通過 context 傳遞一個函數,使得消費組件自行更新 context(場景:在嵌套很深的組件中更新 context ):
theme-context.js
export const ThemeContext = React.createContext({
theme: themes.dark,
toggleTheme: () => {},
});
theme-toggler-button.js
import {ThemeContext} from './theme-context';
// 這個按鈕組件不僅獲取了 theme 值,也從 context 中獲取到一個 toggleTheme 函數
function ThemeTogglerButton() {
return (
//Context.Consumer:基於 context 值渲染React節點
<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 Component {
constructor(props) {
super(props);
//1.定義事件
this.toggleTheme = () => {
this.setState(state => ({
theme:state.theme === themes.dark? themes.light:themes.dark,
}));
};
//2。定義state
this.state = {
theme: themes.light,
toggleTheme: this.toggleTheme,
};
}
render() {
// 3.給Provider的value賦值,包含了當前的主題和事件
return (
<ThemeContext.Provider value={this.state}>
<ThemeTogglerButton />
</ThemeContext.Provider>
);
}
}
示例03-消費多個 Context
為了確保 context 快速進行重渲染,React 需要使每一個 消費組件的 context 在組件樹中成為一個單獨的節點。
App.js
import ProfilePage from './ProfilePage'
// 1.創建主題context
const ThemeContext = React.createContext({
bgColor:'#eee',
fontColor:'balck'
});
// 2.創建用戶context
const UserContext = React.createContext({
name: 'Guest',
});
class App extends Component {
constructor(props){
super(props)
//3.設置在app組件的想要的狀態
this.state = {
theme: {
bgColor:'orange',
fontColor:'white'
},
user: {
name:'Jack'
},
};
}
render() {
const {theme,user}=this.state
//4.給Provider的value傳入值
return (
<ThemeContext.Provider value={theme}>
<UserContext.Provider value={user}>
<Content />
</UserContext.Provider>
</ThemeContext.Provider>
);
}
}
function Content() {
//5.現在可以訪問到Provider傳過來的theme和user
return (
<ThemeContext.Consumer>
{theme => (
<UserContext.Consumer>
{user => (
<ProfilePage user={user} theme={theme} />
)}
</UserContext.Consumer>
)}
</ThemeContext.Consumer>
);
}
export default App
ProfilePage.js
export default function ProfilePage (props){
const {user,theme}=props
return(
//給style傳入了一個對象
<div style={{display:'inline-block',padding:'5px 8px',backgroundColor:theme.bgColor,color:theme.fontColor}}>
{user.name}
</div>
)
}
可在分支10、11、12中獲取示例源碼
