React Hooks 的出现彻底改变了我们编写组件的方式。
它让函数组件拥有了状态管理的能力,让代码更加简洁和可复用。
但 Hooks 不只是一个 API,它背后是一套全新的组件设计哲学。
Hooks 解决了什么问题?
在 Hooks 出现之前,React 有两种组件:函数组件和类组件。
函数组件简单、轻量,但没有状态,只能做"纯展示"的工作。如果你需要状态、生命周期、副作用,就必须写类组件。
类组件有几个明显的问题:
1. **逻辑复用困难**:如果你想在多个组件中共享某些逻辑(比如获取窗口宽度),只能用高阶组件(HOC)或 render props。这些方案会让代码变得复杂,形成"嵌套地狱"。
2. **生命周期割裂**:相关逻辑被分散在 componentDidMount、componentWillUnmount、componentDidUpdate 等多个方法中。比如,一个数据获取的逻辑,需要在 componentDidMount 里开始,在 componentWillUnmount 里取消,在 componentDidUpdate 里处理更新。这种"分散"让代码难以理解和维护。
3. **this 的困惑**:类组件必须理解 `this` 的绑定。为什么事件处理器需要 `bind(this)`?为什么箭头函数可以解决?这些"JavaScript 知识"增加了学习负担。
Hooks 解决了这些问题。
useState:状态管理的简化
useState 是最基础的 Hook:
const [count, setCount] = useState(0);
这看起来很简单,但它改变了状态管理的范式:
1. **状态是局部隔离的**:每个 useState 调用都创建一个独立的状态单元。不像类组件的 state 是一个对象,useState 的状态可以是任何类型——数字、字符串、对象、数组。
2. **状态更新是替换,不是合并**:类组件的 setState 会合并新状态到旧状态,而 useState 的 set 函数是完全替换。这看起来"麻烦",但让状态更新更可控——你明确知道新状态是什么。
3. **命名自由**:useState 返回的变量名完全由你决定。不像类组件必须用 `this.state.xxx`,useState 让你可以用任意名字。
useEffect:副作用的管理
useEffect 是 Hooks 中最复杂但也最强大的部分:
useEffect(() => {
// 副作用逻辑
return () => {
// 清理逻辑
};
}, [依赖数组]);
useEffect 解决了"生命周期割裂"的问题。相关逻辑可以写在一个 useEffect 中:
useEffect(() => {
const fetchData = async () => {
const data = await api.get('/users');
setUsers(data);
};
fetchData();
return () => {
// 取消请求
};
}, []);
这段代码在类组件中需要分散在 componentDidMount 和 componentWillUnmount 两个方法里,但现在可以在一个地方完成。
**依赖数组是关键**:
最佳实践
经过大量实践,我总结了一些 Hooks 的最佳实践:
**1. Hooks 必须在组件顶层调用**
不能在循环、条件语句或嵌套函数中调用 Hooks。这是因为 React 用 Hooks 的调用顺序来匹配状态:
// 错误示例
if (condition) {
const [value, setValue] = useState(0); // ❌
}
// 正确示例
const [value, setValue] = useState(0);
if (condition) {
setValue(1); // ✅
}
**2. 正确设置依赖数组**
依赖数组应该包含所有在 effect 中使用的"外部变量":
// 错误示例:遗漏依赖
useEffect(() => {
fetchUser(userId); // userId 是外部变量
}, []); // ❌ 没有包含 userId
// 正确示例
useEffect(() => {
fetchUser(userId);
}, [userId]); // ✅ 包含 userId
**3. 使用自定义 Hook 复用逻辑**
当你发现多个组件有相同的逻辑时,可以提取为自定义 Hook:
// 自定义 Hook:获取窗口宽度
function useWindowWidth() {
const [width, setWidth] = useState(window.innerWidth);
useEffect(() => {
const handleResize = () => setWidth(window.innerWidth);
window.addEventListener('resize', handleResize);
return () => window.removeEventListener('resize', handleResize);
}, []);
return width;
}
// 在多个组件中使用
function ComponentA() {
const width = useWindowWidth();
// ...
}
function ComponentB() {
const width = useWindowWidth();
// ...
}
写在最后
React Hooks 不只是一个新 API,它是一种新的思维方式。
它让组件逻辑更内聚——相关代码可以放在一起,而不是分散在多个生命周期方法中。它让逻辑复用更简单——自定义 Hook 可以轻松共享逻辑。它让组件更简洁——不再需要类的语法,不再需要 `this` 的绑定。
如果你还在写类组件,建议尝试 Hooks。你会发现:代码变得更简单了,理解变得更容易了,维护变得更轻松了。
Hooks 代表了 React 的未来。掌握它,不只是学习一个新 API,更是拥抱一种更好的组件设计方式。