context + hooks = 真香
文章首发于itsuki.cn个人博客
前言
hooks 打开了新世界的大门,本文教你如何使用 Context + hooks。
后面代码会比较多,主要是扩展思维,坚持看到最后一定会有帮助的!!!
什么是 Context
引用官方文档中的一句话:
Context 提供了一个无需为每层组件手动添加 props,就能在组件树间进行数据传递的方法。
在 React 中当中, 数据流是从上而下的, 而 props 就是数据流中一个重要的载体, 通过 props 传递给子组件渲染出来的对应的视图, 但是如果嵌套层级过深, 需要一层层传递的 props 却显得力不从心, 就像上一篇文章说的prop drilling一样。
一个比较典型的场景就是: 应用程序进行主题、语言切换时, 一层层进行传递想象一下多么的痛苦, 而且你也不可能每一个元素都能够完全覆盖到,而 Context 就提供了一种可以在组件之间共享这些值的方法, 不需要再去显式的传递每一层 props。
基本使用
我们先来说说它的基本使用, 请注意我这里使用的是tsx
, 我平时更加喜欢typescript
+ react
, 体验感更好。
如果我们要创建一个 Context, 可以使用 createContext
方法, 它接收一个参数, 我们举一个简单的例子, 通过这个简单的例子来一点点掌握 context 的用法。
const CounterContext = React.createContext<number>(0);
const App = ({ children }) => {
return (
<CounterContext.Provider value={0}>
<Counter />
</CounterContext.Provider>
);
};
const Counter = () => {
const count = useContext(CounterContext);
return <h1> {count} </h1>;
};
这样子我们的组件无论嵌套多深, 都可以访问到count
这个 props。 但是我们只是实现了访问, 那我们如果要进行更新呢?
如何实现更新
那么我们首先得先改造下参数类型, 从之前的一个联合类型变成一个对象类型, 有两个属性:
- 一个是
count
表示数量。 - 另一个是
setCount
实现更新数量的函数。
export type CounterContextType = {
count: number;
setCount: Dispatch<SetStateAction<number>>;
};
const CounterContext = React.createContext<CounterContextType | undefined>(undefined);
初始化时, 使用默认参数undefined
进行占位, 那我们的 App 组件也要进行相对应的修改。
最终的代码就是这样子:
const App = ({ children }) => {
const [count, setCount] = useState(0);
return (
<CounterContext.Provider value={
{ count, setCount }}>
<Counter />
</CounterContext.Provider>
);
};
const Counter = () => {
const { count, setCount } = useContext(CounterContext);
return (
<div>
<button onClick={() => setCount(count + 1)}>+1</button>
<button onClick={() => setCount(count - 1)}>-1</button>
</div>
);
};
看起来很棒,我们实现了计数器。
那我们来看看加上 hooks 的 Context 可以怎么优化, 现在开始准备起飞了。
ContextProvider
我们可以先封装一下CounterContext.Provider
, 我希望App
组件只是将所有组件做一个组合, 而不是做状态管理, 想象一下你的App
组件有 n 个 Context(UIContext、AuthContext、ThemeContext 等等), 如果在这里进行状态管理那是多么痛苦的事情, 而App
组件也失去了组合的意义。
const App = ({ children }) => {
const [ui, setUI] = useState();
const [auth, setAuth] = useState();
const [theme, setTheme] = useState();
return (
<UIContext.Provider value={
{ ui, setUI }}>
<AuthContext.Provider value={
{ au