useContext
useContext 用于保存全局上下文信息,我们可以在其中放入公共的对象,供所有子组件使用,比如字典、鉴权信息
细心的读者可能会发现,我的教程没有按照 React 官方的 Hook 介绍顺序来,为啥呢,因为我觉得单独讲 useContext 意义不大,它要配合其它 hook 一起使用才能发挥最大价值
官方教程的写法太简单,实际中一般不会这么用,这里给出我们实际项目中的写法,综合使用了之前教程介绍的 hook
Context.tsx
import React from "react";
export type TContext = {
count: number;
setCount: React.Dispatch<React.SetStateAction<TContext["count"]>>;
};
const Context = React.createContext<TContext | null>(null);
Context.displayName = "Context";
export const useContextValue = () => {
const value = React.useContext(Context);
if (value === null) {
throw new Error("value is null");
}
return value;
};
export const Provider: React.FC = (props) => {
const [count, setCount] = React.useState<TContext["count"]>(0);
const value = React.useMemo(() => {
return {
count,
setCount,
};
}, [count, setCount]);
return <Context.Provider value={value}>{props.children}</Context.Provider>;
};
export default Provider;
Context 文件做了以下工作:
- 使用 useState 存取值,TContext 定义保存的数据类型
- 使用 useMemo 缓存值,避免全局值每次改变时都触发子组件重新渲染(在有多个值时很有用,这里为了简单只写了一个值)
- 提供 Provider 组件给父组件使用,封装缓存逻辑
- 提供 useContextValue 函数给子组件使用,减少样板代码
App.tsx
import React from "react";
import "./App.css";
import Provider, { useContextValue } from "./Context";
const App: React.FC = () => {
return (
<Provider>
<Son />
</Provider>
);
};
const Son: React.FC = () => {
const { count } = useContextValue();
return (
<>
<div>count: {count}</div>
<Grandson />
</>
);
};
const Grandson: React.FC = () => {
const { setCount } = useContextValue();
return <button onClick={() => setCount((pre) => pre + 1)}>点击增加</button>;
};
export default App;
这里为了方便新手理解,父组件、子组件、孙组件都写在了一个文件里,实际项目中应该一个文件对应一个组件
- 父组件 App,使用 Provider 包裹子组件
- 子组件 Son,使用 useContextValue 获取 count 值
- 孙组件 Grandson,使用 useContextValue 设置 count 值
可以看到,子组件可以方便地获取、设置全局上下文中的值,任意数量、任意深度的子组件都可以使用
运行效果如下: