以一个主题切换的例子,来说明useContext的基本使用
1.创建Context
const ThemeContext = React.createContext<IThemeContext>({
theme: Theme.Dark,
// eslint-disable-next-line @typescript-eslint/no-empty-function
// TODO: 这里想声明一个空函数, 该如何写?
setTheme: (theme) => {
},
});
2.创建组件ThemeProvider, 返回ThemeContext.Provider,在组件中使用useReducer来声明公共状态
function ThemeProvider({ children }: { children: React.ReactNode }) {
const [theme, dispatch] = useReducer(reducer, Theme.Light);
function reducer(state: Theme, action: { type: Theme }) {
console.log('prevState=>', state);
console.log('action=>', action);
let nextState;
switch (action.type) {
case Theme.Light:
nextState = Theme.Dark;
break;
case Theme.Dark:
nextState = Theme.Light;
break;
default:
nextState = state;
}
console.log('nextState=>', nextState);
return nextState;
}
return (
<ThemeContext.Provider value={{ theme, setTheme: dispatch }}>
<button
onClick={() => {
dispatch({
type: theme,
});
}}
>
切换主题-ThemeWarp
</button>
{children}
</ThemeContext.Provider>
);
}
3.创建使用/不使用context状态的组件、切换主题按钮和一个主要组件
优化点:由于context值更新,会导致Provider中所有子组件重新渲染,我们需要尽量提升渲染无关的子组件到Provider外部
// 不使用context
function ChildNonTheme() {
console.log('不关心皮肤的子组件渲染了');
return (
<div>
<p>我不关心皮肤,主题色改变时不要让我渲染</p>
</div>
);
}
// 使用context
function ChildWithTheme() {
const { theme, setTheme } = useContext(ThemeContext);
console.log('我关心皮肤,主题改变时,我随之改变');
return (
<div>
<p>我是有皮肤的哦~ {theme}</p>
<button
onClick={() => {
setTheme({
type: theme === Theme.Dark ? Theme.Light : Theme.Dark,
});
}}
>
切换主题-ChildWithTheme
</button>
</div>
);
}
// 切换context值的button
function ToggleThemeButton() {
const { theme, setTheme } = useContext(ThemeContext);
return (
<button
onClick={() => {
setTheme({
type: theme,
});
}}
>
Toggle Theme Button
</button>
);
}
// 入口组件
export default function ThemeDemo() {
return (
// Context 的 value 更新 Provider中的子组件都会重新渲染
// 我们需要拆分,涉及更新的组件到对应的Context下
<>
<div>
<h3>theme useReducer优化</h3>
<ThemeProvider>
{/* 在Provider里面useContext才管用!!!!!!!! */}
<ChildWithTheme/>
<ToggleThemeButton/>
</ThemeProvider>
{/* 错误:Provider外是无效的 */}
<ToggleThemeButton/>
<ChildNonTheme/>
<ChildNonTheme/>
<ChildNonTheme/>
<ChildNonTheme/>
<ChildNonTheme/>
<ChildNonTheme/>
<ChildNonTheme/>
<ChildNonTheme/>
<ChildNonTheme/>
</div>
</>
);
}
注:useContext使用时,必须在ThemeContext.Provider组件内部才有效。