在react中,context是一種無需為每層組件手動添加props就能在組件樹之間進行資料傳遞的方法;context提供了一種在組件之間共享指定值的方式,而且不必顯式的通過組件樹的逐層傳遞props。
本教學操作環境:Windows10系統、react17.0.1版、Dell G3電腦。
Context 提供了一個不需要為每層元件手動新增 props,就能在元件樹間進行資料傳遞的方法。在典型的React 應用程式中,資料是透過props 屬性自上而下(由父及子)進行傳遞的,但這種做法對於某些類型的屬性而言是極其繁瑣的(例如:地區偏好,UI主題),這些屬性是應用程式中許多元件都需要的。 Context 提供了一種在元件之間共用此類值的方式,而不必明確地透過元件樹的逐層傳遞 props。
Context 設計目的是為了共享那些對於一個元件樹而言是「全域」的數據,例如當前認證的使用者、主題或首選語言。舉個例子,在下面的程式碼中,我們透過一個「theme」 屬性手動調整一個按鈕元件的樣式
class App extends React.Component { render() { return <Toolbar theme="dark" />; } } function Toolbar(props) { // Toolbar 组件接受一个额外的“theme”属性,然后传递给 ThemedButton 组件。 // 如果应用中每一个单独的按钮都需要知道 theme 的值,这会是件很麻烦的事, // 因为必须将这个值层层传递所有组件。 return ( <p> <ThemedButton theme={props.theme} /> </p> ); } class ThemedButton extends React.Component { render() { return <Button theme={this.props.theme} />; } } // 通过props传递:App -> Toolbar -> ThemedButton // 如果嵌套很深,那么需要逐层传递props,即使中间不需要该props,显得很繁琐
使用context, 我們可以避免透過中間元素傳遞props
// 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() { return ( <p> <ThemedButton /> </p> ); } class ThemedButton extends React.Component { // 指定 contextType 读取当前的 theme context。 // React 会往上找到最近的 theme Provider,然后使用它的值。 // 在这个例子中,当前的 theme 值为 “dark”。 static contextType = ThemeContext; render() { return <Button theme={this.context} />; } } // 也可以使用 ThemedButto.contextType = ThemeContext;
React.createContext
const MyContext = React.createContext(defaultValue);
建立一個Context 物件。當 React 渲染一個訂閱了這個 Context 物件的元件,這個元件會從元件樹中離自身最近的那個符合的 Provider
中讀取到目前的 context 值。
只有當元件所處的樹中沒有符合到 Provider 時,其 defaultValue
參數才會生效。這有助於在不使用 Provider 包裝組件的情況下對組件進行測試。注意:將 undefined
傳遞給 Provider 的 value 時,消費元件的 defaultValue
不會生效。
Context.Provider
<MyContext.Provider value={/* 某个值 */}>
每個 Context 物件都會傳回一個 Provider React 元件,它允許消費元件訂閱 context 的變更。
Provider 接收一個 value
屬性,傳遞給消費元件。一個 Provider 可以和多個消費元件有對應關係。多個 Provider 也可以嵌套使用,裡層的會覆蓋外層的資料。
當 Provider 的 value
值改變時,它內部的所有消費元件都會重新渲染。 Provider 及其內部 consumer 元件都不受制於 shouldComponentUpdate
函數,因此當 consumer 元件在其祖先元件退出更新的情況下也能更新。
Class.contextType
contextType 屬性會被重賦值為一個由 React.createContext() 所建立的Context對象。這能讓你使用
this.context 來消費最近 Context 上的那個價值。你可以在任何生命週期中存取到它,包括 render 函數中
import MyContext from './MyContext'; class MyClass extends React.Component { componentDidMount() { let value = this.context; /* 在组件挂载完成后,使用 MyContext 组件的值来执行一些有副作用的操作 */ } componentDidUpdate() { let value = this.context; /* ... */ } componentWillUnmount() { let value = this.context; /* ... */ } render() { let value = this.context; /* 基于 MyContext 组件的值进行渲染 */ } // 或者如上边例子一样使用 static contextType = MyContext; } MyClass.contextType = MyContext;
import MyContext from './MyContext'; function ToolList() { return ( <MyContext.Consumer {value => /* 基于 context 值进行渲染*/} </MyContext.Consumer> ) }
value 值等同於往上元件樹離這個 context 最近的 Provider 所提供的
value 值。如果沒有對應的 Provider,
value 參數等同於傳給
createContext() 的
defaultValue。
displayName 的 property,類型為字串。 React DevTools 使用該字串來決定 context 要顯示的內容。
const MyContext = React.createContext(/* some value */); MyContext.displayName = 'MyDisplayName'; <MyContext.Provider> // "MyDisplayName.Provider" 在 DevTools 中 <MyContext.Consumer> // "MyDisplayName.Consumer" 在 DevTools 中
export const themes = {
light: {
foreground: '#000000',
background: '#eeeeee',
},
dark: {
foreground: '#ffffff',
background: '#222222',
},
};
export const ThemeContext = React.createContext(themes.dark); // 该处为默认值
import { ThemeContext } from './theme-context';
class ThemedButton extends React.Component {
render() {
let props = this.props;
// 获取到ThemeContext中的默认值
let theme = this.context;
return (
<button
{...props}
style={{backgroundColor: theme.background}}
/>
);
}
// static contextType = ThemeContext;
}
ThemedButton.contextType = ThemeContext;
export default ThemedButton;
# 在上面的例子中,我们通过 props 的方式向下传递一个更新函数,从而改变 App 中 themes 的值。我们知道,从一个在组件树中嵌套很深的组件中更新 context 是很有必要的。在这种场景下,你可以通过 context 传递一个函数,使得 consumers 组件更新 context theme-context.js theme-toggler-button.js app.js 为了确保 context 快速进行重渲染,React 需要使每一个 consumers 组件的 context 在组件树中成为一个单独的节点 如果两个或者更多的 context 值经常被一起使用,那你可能要考虑一下另外创建你自己的渲染组件,以提供这些值。 因为 context 会使用参考标识(reference identity)来决定何时进行渲染,这里可能会有一些陷阱,当 provider 的父组件进行重渲染时,可能会在 consumers 组件中触发意外的渲染。举个例子,当每一次 Provider 重渲染时,以下的代码会重渲染所有下面的 consumers 组件,因为 为了防止这种情况,将 value 状态提升到父节点的 state 里 【相关推荐:javascript视频教程、web前端】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() {
// 在 ThemeProvider 内部的 ThemedButton 按钮组件使用 state 中的 theme 值,
// 而外部的组件使用默认的 theme 值
return (
<Page>
<ThemeContext.Provider value={this.state.theme}>
<Toolbar changeTheme={this.toggleTheme} />
</ThemeContext.Provider>
<Section>
<ThemedButton />
</Section>
</Page>
);
}
}
ReactDOM.render(<App />, document.root);
// 使用ThemeContext.Provider包裹的组件,可以消费到ThemeContext中的value
// 即Toolbar、ThemedButton中都可以使用this.context来获取到value
// 注意观察,更新state的方法是通过props向下传递,由子孙组件触发更新,下面会讲到通过context的方式传递更新函数
在嵌套组件中更新 Context
// 确保传递给 createContext 的默认值数据结构是调用的组件(consumers)所能匹配的!
export const ThemeContext = React.createContext({
theme: themes.dark,
toggleTheme: () => {}, // 定义更新主题的方法,向下传递
});
import { ThemeContext } from './theme-context';
function ThemeTogglerButton() {
// Theme Toggler 按钮不仅仅只获取 theme 值,它也从 context 中获取到一个 toggleTheme 函数(下面app.js部分)
return (
<ThemeContext.Consumer>
{({theme, toggleTheme}) => (
<button onClick={toggleTheme} style={{backgroundColor: theme.background}}>
Toggle Theme
</button>
)}
</ThemeContext.Consumer>
);
}
export default ThemeTogglerButton;
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 也包含了更新函数,因此它会被传递进 context provider。
this.state = {
theme: themes.light,
toggleTheme: this.toggleTheme, // 定义更新函数,通过context方式向下传递
};
}
render() {
// 整个 state 都被传递进 provider
return (
<ThemeContext.Provider value={this.state}>
<Content />
</ThemeContext.Provider>
);
}
}
function Content() {
return (
<p>
<ThemeTogglerButton />
</p>
);
}
ReactDOM.render(<App />, document.root);
消费多个 Context
// Theme context,默认的 theme 是 "light" 值
const ThemeContext = React.createContext('light');
// 用户登录 context
const UserContext = React.createContext({
name: 'Guest',
});
class App extends React.Component {
render() {
const { signedInUser, theme } = this.props;
// 提供初始 context 值的 App 组件
return (
<ThemeContext.Provider value={theme}>
<UserContext.Provider value={signedInUser}>
<Layout />
</UserContext.Provider>
</ThemeContext.Provider>
);
}
}
function Layout() {
return (
<p>
<Sidebar />
<Content />
</p>
);
}
// 一个组件可能会消费多个 context
function Content() {
return (
<ThemeContext.Consumer>
{theme => (
<UserContext.Consumer>
{user => (
<ProfilePage user={user} theme={theme} />
)}
</UserContext.Consumer>
)}
</ThemeContext.Consumer>
);
}
注意事项
value
属性总是被赋值为新的对象class App extends React.Component {
render() {
return (
<MyContext.Provider value={{something: 'something'}}>
<Toolbar />
</MyContext.Provider>
);
}
}
class App extends React.Component {
constructor(props) {
super(props);
// 多次渲染,state 会被保留,当value不变时,下面的 consumers 组件不会重新渲染
this.state = {
value: {something: 'something'},
};
}
render() {
return (
<Provider value={this.state.value}>
<Toolbar />
</Provider>
);
}
}
以上是react中context是什麼的詳細內容。更多資訊請關注PHP中文網其他相關文章!