在React的世界里,曾经有一段时间,Class(类)组件是构建动态、有状态应用的不二法门。它们强大、可靠,支撑起了无数复杂的界面。然而,随着应用逻辑的日益复杂,开发者们也逐渐感受到了类组件带来的一些困扰。
小希就是这样一位开发者,她正在维护一个几年前用React构建的项目。她发现,仅仅是一个简单的功能,逻辑就可能散落在componentDidMount、componentDidUpdate和componentWillUnmount等多个生命周期方法中。更让她头疼的是,组件内部this的指向问题,总是在不经意间导致bug。她感觉自己不像在写功能,更像是在"驯服"一个难以捉摸的类。
纠缠的逻辑:一个典型的Class组件
让我们看看小希遇到的一个典型场景:一个组件需要根据传入的用户ID,订阅该用户的在线状态,并在组件销毁时取消订阅。
用Class组件实现,代码看起来是这样的:
javascript
class FriendStatus extends React.Component {
constructor(props) {
super(props);
this.state = { isOnline: null };
this.handleStatusChange = this.handleStatusChange.bind(this);
}
componentDidMount() {
ChatAPI.subscribeToFriendStatus(
this.props.friend.id,
this.handleStatusChange
);
}
componentWillUnmount() {
ChatAPI.unsubscribeFromFriendStatus(
this.props.friend.id,
this.handleStatusChange
);
}
handleStatusChange(status) {
this.setState({
isOnline: status.isOnline
});
}
render() {
if (this.state.isOnline === null) {
return 'Loading...';
}
return this.state.isOnline ? 'Online' : 'Offline';
}
}
问题显而易见:订阅 和取消订阅 这两个紧密相关的逻辑,被人为地拆分到了componentDidMount和componentWillUnmount两个完全不同的方法里。当组件逻辑变得更复杂时,这种"分裂"会愈发严重,导致代码难以理解和维护。
豁然开朗:useState 与 useEffect 的登场
就在小希为此感到困惑时,她发现了React Hooks。这是一种全新的理念,它允许开发者在不编写类的情况下使用state以及其它React特性。其中,最核心的两个Hooks就是useState和useEffect。
useState:让函数拥有状态
useState彻底告别了this.state和this.setState。它让函数组件可以直接拥有自己的状态。
javascript
import React, { useState } from 'react';
function Counter() {
// 声明一个名为 count 的 state 变量,初始值为 0
const [count, setCount] = useState(0);
return (
<div>
<p>You clicked {count} times</p>
<button onClick={() => setCount(count + 1)}>
Click me
</button>
</div>
);
}
代码变得无比直观,状态的声明和更新都在一处,不再需要关心this。
useEffect:处理"副作用"的瑞士军刀
useEffect则更具革命性,它统一了过去分散的生命周期概念。数据获取、设置订阅、以及手动更改DOM等操作,都被视为"副作用"(Side Effects),由useEffect来处理。
现在,让我们用Hooks来重写小希的那个状态订阅组件:
javascript
import React, { useState, useEffect } from 'react';
function FriendStatus(props) {
const [isOnline, setIsOnline] = useState(null);
useEffect(() => {
function handleStatusChange(status) {
setIsOnline(status.isOnline);
}
ChatAPI.subscribeToFriendStatus(props.friend.id, handleStatusChange);
// 这个 return 的函数就是"清理"函数
return function cleanup() {
ChatAPI.unsubscribeFromFriendStatus(props.friend.id, handleStatusChange);
};
}); // 每次渲染后都会执行effect
if (isOnline === null) {
return 'Loading...';
}
return isOnline ? 'Online' : 'Offline';
}
奇迹发生了!订阅 和取消订阅 的逻辑,现在被完美地组织在了一个useEffect内部。useEffect返回的那个函数,React会在组件销毁时自动执行它,用于"清理"副作用。所有相关的代码都待在了它们应该在的地方,逻辑变得清晰、自洽。这种代码组织方式,让一切都变得井井有条。

终极魔法:封装逻辑的自定义Hook
Hooks真正的威力在于它的可组合性。开发者可以将组件中一些相关的、可复用的状态逻辑,抽离成一个独立的"自定义Hook"(Custom Hook)。自定义Hook本质上只是一个函数,但它的名字必须以use开头。
小希可以将刚才的状态订阅逻辑,封装成一个useFriendStatus的自定义Hook:
javascript
import { useState, useEffect } from 'react';
function useFriendStatus(friendID) {
const [isOnline, setIsOnline] = useState(null);
useEffect(() => {
function handleStatusChange(status) {
setIsOnline(status.isOnline);
}
ChatAPI.subscribeToFriendStatus(friendID, handleStatusChange);
return () => {
ChatAPI.unsubscribeFromFriendStatus(friendID, handleStatusChange);
};
});
return isOnline;
}
有了这个自定义Hook,原来的组件就可以被简化成这样:
javascript
function FriendStatus(props) {
const isOnline = useFriendStatus(props.friend.id);
if (isOnline === null) {
return 'Loading...';
}
return isOnline ? 'Online' : 'Offline';
}
组件本身不再关心订阅的内部实现,它只负责使用useFriendStatus这个Hook来获取状态并渲染UI。逻辑被完美地解耦和复用。想要深入了解更多Hooks的用法,官方文档是最好的起点。
React官方文档:https://react.dev/
结语:一种全新的思考方式
从Class组件到Hooks,这不仅仅是API的改变,更是一种编程思想的转变。它引导开发者用函数式、组合式的方式去思考和组织UI逻辑,而不是陷入复杂的继承和生命周期管理中。
对于像小希这样的开发者来说,Hooks就像一扇窗,让她看到了一个更简洁、更清晰的React世界。如今,Hooks已经成为React社区的主流和未来,无论是开始一个新项目,还是维护旧项目,它都是值得拥抱的新范式。
许多优秀的UI库也早已全面拥抱Hooks。
MUI (原Material-UI)官网:https://mui.com/