告别 Class 组件:拥抱 React Hooks 带来的函数式新范式

在React的世界里,曾经有一段时间,Class(类)组件是构建动态、有状态应用的不二法门。它们强大、可靠,支撑起了无数复杂的界面。然而,随着应用逻辑的日益复杂,开发者们也逐渐感受到了类组件带来的一些困扰。

小希就是这样一位开发者,她正在维护一个几年前用React构建的项目。她发现,仅仅是一个简单的功能,逻辑就可能散落在componentDidMountcomponentDidUpdatecomponentWillUnmount等多个生命周期方法中。更让她头疼的是,组件内部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';
  }
}

问题显而易见:订阅取消订阅 这两个紧密相关的逻辑,被人为地拆分到了componentDidMountcomponentWillUnmount两个完全不同的方法里。当组件逻辑变得更复杂时,这种"分裂"会愈发严重,导致代码难以理解和维护。

豁然开朗:useStateuseEffect 的登场

就在小希为此感到困惑时,她发现了React Hooks。这是一种全新的理念,它允许开发者在不编写类的情况下使用state以及其它React特性。其中,最核心的两个Hooks就是useStateuseEffect

useState:让函数拥有状态

useState彻底告别了this.statethis.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/

相关推荐
Pedantic3 小时前
SwiftUI 手势层级(Gesture Hierarchy)详解
前端
飘尘3 小时前
前端转型全栈(Java后端)的快速上手指引
前端·后端·全栈
一颗烂土豆3 小时前
Meshopt 压缩深度解析,为什么它比 Draco 更快
前端·javascript·webgl
YFF菲菲兔4 小时前
调度系统和调和系统的桥梁
react.js
浏览器工程师4 小时前
AI Agent 接浏览器任务,先别让它一路点到底
前端·后端
雨季mo浅忆4 小时前
VSCode自动格式化三要素
前端
爱勇宝5 小时前
深扒 Anthropic 1680 位工程师简历:应届生几乎没机会,AI 公司最缺的不是博士
前端·后端·程序员
kyriewen5 小时前
同事每天催我 Code Review,我写了个脚本让 AI 替我 review PR——现在他反过来催 AI 了
前端·javascript·ai编程
user20585561518138 小时前
Windows 项目安装时报 `node-sass` 错误,如何快速处理
前端