复杂情况下的组件通信Hooks:useContext
您好,我是沧沧凉凉,是一名前端开发者,目前在掘金、知乎以及个人博客上同步发表一些学习前端时遇到的趣事和知识,欢迎关注。
对于一个中后台产品来说,其实Redux显得不是那么有必要,因为它操作复杂和繁琐,而且由于Hooks的发布,让Redux有了更多的替代方案。
在中后台产品中很多数据都仅仅只在当前页面进行展示,几乎不会出现这里获取数据,放在一个完全不同的地方进行展示。如果使用Redux的话还有一定的副作用,就是在Redux中的数据,如果你不手动重置或者刷新页面,它会一直存在。而这个时候使用Context就显得更加方便。
关于Redux可以看TypeScript下使用Hooks的方式重新学习Redux和React-Redux。
Context包裹中的组件都可以访问到Context中的状态,无论是子组件,孙组件,兄弟组件,无论层级有多深,只要被Context包裹,都能够获取到Context中的状态,所以在写复杂页面的时候Context几乎是你的不二选择。
1. 使用useContext
官方对于useContext的介绍非常简单,可能看了后有一些疑惑,今天我就主要以官方的代码来说一下useContext到底应该怎么用。
// 这里是需要使用到的数据
const themes = {
light: {
foreground: "#000000",
background: "#eeeeee"
},
dark: {
foreground: "#ffffff",
background: "#222222"
}
};
// 1、这里是生成一个Context
const ThemeContext = React.createContext(themes.light);
function App() {
return (
// 2、传递一个Context而value中就是传入的值
<ThemeContext.Provider value={themes.dark}>
<Toolbar />
</ThemeContext.Provider>
);
}
function Toolbar(props) {
return (
<div>
<ThemedButton />
</div>
);
}
function ThemedButton() {
// 3、消费Context
const theme = useContext(ThemeContext);
return (
<button style={{ background: theme.background, color: theme.foreground }}>
I am styled by theme context!
</button>
);
}
其实useContext的使用步骤可以分为3步,分别为:
- 生成一个Context。
- 使用生成的Context对象将所有可能需要用到Context的组件进行包裹(只要被包裹,无论层级,都可以消费Context)。
- 使用useContext进行消费。
清楚了这3个步骤后我们再回过头看一下上面的代码,第一个步骤,生成Context。
const ThemeContext = React.createContext(themes.light);
React.createContext的括号中的东西我个人理解为数据模型,其实你传什么都可以,你传入一个null
也无所谓,都可以正确的消费Context,有些人就喜欢在项目中全局定义一个Context对象,就像下面这么写:
export const ThemeContext = React.createContext(null);
然后所有地方都是用这个Context对象进行包裹,当然我个人是不建议这么做的,因为一旦你这么做,你消费Context的时候就无法得到正确的代码提示。
可以看到上面的数据传递进去不是响应式的,就算更改了数据页面也不会刷新,其实Context对象中value的值也可以为state,例如下面这个计数器。
2. TS中使用useContext
TS和React简直是绝配,在React中能够完全发挥出TS的最大功效,而Context也是这样。
import React, { useContext, useState } from "react";
// 声明Context的数据模型
interface CountProps {
count: number;
setCount: React.Dispatch<React.SetStateAction<number>>;
}
// 1、创建一个Context对象
const ThemeContext = React.createContext({} as CountProps);
function ThemedButton() {
// 3、也可以直接使用解构来获取数据
const { count, setCount } = useContext(ThemeContext);
return (
<>
{count}
<button
onClick={() => {
setCount((value) => value + 1);
}}
>
点我
</button>
</>
);
}
function Toolbar() {
// 3、消费Context
const theme = useContext(ThemeContext);
return (
<div>
<button
onClick={() => {
theme.setCount((value) => value + 1);
}}
>
点我
</button>
<ThemedButton />
</div>
);
}
export default function App() {
const [count, setCount] = useState(0);
return (
// 2、可以将count和setCount传入子组件中
<ThemeContext.Provider value={{ count, setCount }}>
<Toolbar />
</ThemeContext.Provider>
);
}
使用TS的类型声明可以直接对数据模型进行定义,而在消费Context的时候就会获得正确的类型提示。
3. 最后
以上就是useContext的基础用法了,也许在中后台项目中Context不会经常使用,大部分情况还是通过Props进行参数传递,但是一旦出现层级比较深的情况,或者兄弟之间需要使用同一个状态,那么使用Context会大大降低这些页面搭建的复杂度。
本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!