告别 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/

相关推荐
郭老二1 小时前
【前端】vue3:编译步骤
前端
不在逃避q2 小时前
Trae/Vs Code/Cursor命令行无法跑npm命令
前端·arcgis·npm
夏殇之殁2 小时前
包中创建自定义列表项。 . 使用自定义列表项进行数据绑定 . 将天气预报数据保存到本地内存表,通过LiveBindings进行显示。 ...
服务器·前端·javascript
The Chosen One9852 小时前
高进度算法模板速记(待完善)
java·前端·算法
陈随易2 小时前
MoonBit抓包模块pcap,查看电脑的每一次联网通信
前端·后端·程序员
新中地GIS开发老师3 小时前
WebGIS开发学生作品|低空航天管理与航线规划系统
前端·javascript·webgis·三维gis开发
用户059540174463 小时前
大模型对话记忆持久化踩坑实录:用 Playwright 自动化测了 300 次,终于揪出会话丢失的真凶
前端·css
丙氨酸長鏈3 小时前
Web前端入门第 问:JavaScript 一个简单的 IndexedDB 数据库入门示例
前端·javascript·数据库
kyriewen4 小时前
面试官让我手写虚拟列表——AI生成的版本,快速滚动几下就白屏了
前端·javascript·面试
IT_陈寒5 小时前
Vite热更新失效?你可能漏了这个配置
前端·人工智能·后端