Context 提供了一个无需为每层组件手动添加 props ,就能在组件树间进行数据传递的方法,useContext
用于函数组件中订阅上层 context 的变更,可以获取上层 context 传递的 value
prop 值
useContext
接收一个 context 对象(React.createContext
的返回值)并返回 context 的当前值,当前的 context 值由上层组件中距离当前组件最近的 <MyContext.Provider>
的 value
prop 决定
const value = useContext(MyContext);
使用:
import React, { useContext, useState } from 'react';
const themes = {
light: {
foreground: "#000000",
background: "#eeeeee"
},
dark: {
foreground: "#ffffff",
background: "#222222"
}
};
// 为当前 theme 创建一个 context
const ThemeContext = React.createContext();
export default function Toolbar(props) {
const [theme, setTheme] = useState(themes.dark);
const toggleTheme = () => {
setTheme(currentTheme => (
currentTheme === themes.dark
? themes.light
: themes.dark
));
};
return (
// 使用 Provider 将当前 props.value 传递给内部组件
<ThemeContext.Provider value={{theme, toggleTheme}}>
<ThemeButton />
</ThemeContext.Provider>
);
}
function ThemeButton() {
// 通过 useContext 获取当前 context 值
const { theme, toggleTheme } = useContext(ThemeContext);
return (
<button style={{background: theme.background, color: theme.foreground }} onClick={toggleTheme}>
Change the button's theme
</button>
);
}
等价 class的示例,如下:
useContext(MyContext)
相当于 class 组件中的 static contextType = MyContext
或者 <MyContext.Consumer>
useContext
并没有改变消费 context 的方式,它只为我们提供了一种额外的、更漂亮的、更漂亮的方法来消费上层 context。在将其应用于使用多 context 的组件时将会非常有用
import React from 'react';
const themes = {
light: {
foreground: "#000000",
background: "#eeeeee"
},
dark: {
foreground: "#ffffff",
background: "#222222"
}
};
const ThemeContext = React.createContext(themes.light);
function ThemeButton() {
return (
<ThemeContext.Consumer>
{
({theme, toggleTheme}) => (
<button style={{background: theme.background, color: theme.foreground }} onClick={toggleTheme}>
Change the button's theme
</button>
)
}
</ThemeContext.Consumer>
);
}
export default class Toolbar extends React.Component {
constructor(props) {
super(props);
this.state = {
theme: themes.light
};
this.toggleTheme = this.toggleTheme.bind(this);
}
toggleTheme() {
this.setState(state => ({
theme:
state.theme === themes.dark
? themes.light
: themes.dark
}));
}
render() {
return (
<ThemeContext.Provider value={{ theme: this.state.theme, toggleTheme: this.toggleTheme }}>
<ThemeButton />
</ThemeContext.Provider>
)
}
}
优化消费 context 组件:
调用了 useContext
的组件都会在 context 值变化时重新渲染,为了减少重新渲染组件的较大开销,可以通过使用 memoization 来优化
假设由于某种原因,您有 AppContext
,其值具有 theme
属性,并且您只想在 appContextValue.theme
更改上重新渲染一些 ExpensiveTree
-
方式1: 拆分不会一起更改的 context
-
当不能拆分 context 时,将组件一分为二,给中间组件加上
React.memo
-
返回一个内置
useMemo
的组件function Button() {
// 把 theme context 拆分出来,其他 context 变化时不会导致 ExpensiveTree 重新渲染
let theme = useContext(ThemeContext);
return <ExpensiveTree className={theme} />;
}function Button() {
let appContextValue = useContext(AppContext);
let theme = appContextValue.theme; // 获取 theme 属性
return <ThemedButton theme={theme} />
}const ThemedButton = memo(({ theme }) => {
// 使用 memo 尽量复用上一次渲染结果
return <ExpensiveTree className={theme} />;
});function Button() {
let appContextValue = useContext(AppContext);
let theme = appContextValue.theme; // 获取 theme 属性return useMemo(() => {
// The rest of your rendering logic
return <ExpensiveTree className={theme} />;
}, [theme])
}