React 从类组件到 Hooks:为什么 Hooks 改变了 React?

学习 React 时,经常会看到两种写法:

scala 复制代码
class App extends React.Component {}

csharp 复制代码
function App() {}

很多新手会疑惑:

复制代码
为什么 React 有两种组件?

为什么现在项目几乎都在使用函数组件?

Hooks 又是什么?

想搞懂这些问题,就需要了解 React 的发展历史。


React 16.8 之前:类组件时代

在 React 16.8 之前:

swift 复制代码
函数组件
=
UI展示

类组件
=
真正开发业务

原因很简单:

复制代码
只有类组件拥有状态和生命周期

因此绝大部分业务代码都写在类组件中。


什么是类组件?

类组件本质上是一个 JavaScript Class。

例如:

scala 复制代码
import React from 'react'

class App extends React.Component {

  render() {
    return (
      <h1>Hello React</h1>
    )
  }

}

export default App

React 会创建类的实例:

scss 复制代码
new App()

然后渲染页面。


什么是状态(State)?

React 中最重要的概念之一:

复制代码
State(状态)

状态可以理解为:

复制代码
数据发生变化

组件会重新渲染

例如:

复制代码
计数器数字

用户信息

购物车数量

主题颜色

这些都属于状态。


类组件中的状态

类组件使用:

kotlin 复制代码
this.state

保存状态。

例如:

scala 复制代码
class App extends React.Component {

  state = {
    count: 0
  }

  render() {
    return (
      <h1>{this.state.count}</h1>
    )
  }
}

为什么不能直接修改 state?

错误写法:

kotlin 复制代码
this.state.count++

React 不会更新页面。

因为:

复制代码
React无法感知变化

setState

React 提供:

kotlin 复制代码
this.setState()

修改状态。

例如:

kotlin 复制代码
this.setState({
  count: 1
})

执行过程:

复制代码
修改状态

↓

触发重新渲染

↓

更新页面

因此:

ini 复制代码
setState
=
修改状态

+
触发更新

生命周期

类组件最大的特点之一:

复制代码
生命周期(Lifecycle)

组件从出生到销毁:

复制代码
创建

↓

运行

↓

更新

↓

销毁

React 在这些阶段提供对应钩子函数。


componentDidMount

组件挂载完成后执行。

javascript 复制代码
componentDidMount() {

}

触发时机:

复制代码
页面首次渲染完成

常见用途:

复制代码
发送请求

获取数据

订阅事件

开启定时器

例如:

scss 复制代码
componentDidMount(){

  fetch('/api/user')

}

componentDidUpdate

组件更新后执行。

javascript 复制代码
componentDidUpdate() {

}

触发时机:

perl 复制代码
state变化

props变化

之后执行。


例如:

复制代码
数据更新

↓

组件重新渲染

↓

componentDidUpdate

componentWillUnmount

组件销毁前执行。

javascript 复制代码
componentWillUnmount() {

}

常见用途:

复制代码
清除定时器

解绑事件

取消订阅

例如:

scss 复制代码
componentWillUnmount(){

  clearInterval(timer)

}

类组件的问题

随着项目越来越大:

kotlin 复制代码
生命周期逻辑分散

this容易丢失

代码复用困难

逻辑组织混乱

例如:

复制代码
获取数据

↓

componentDidMount

处理更新

↓

componentDidUpdate

清理资源

↓

componentWillUnmount

同一个功能被拆散到多个地方。

维护成本越来越高。


React 16.8:Hooks 诞生

2019年 React 发布:

复制代码
React Hooks

这是 React 历史上最重要的更新之一。

目标:

复制代码
让函数组件拥有类组件的能力

从此:

diff 复制代码
函数组件

=

状态

+

生命周期

+

逻辑复用

函数组件时代

现代 React 项目几乎全部采用:

csharp 复制代码
function App() {

}

写法。

例如:

javascript 复制代码
function App() {

  return (
    <h1>Hello React</h1>
  )

}

Hooks 是什么?

官方定义:

复制代码
Hook 是特殊函数

允许函数组件使用 React 特性

最常用的两个 Hook:

复制代码
useState

useEffect

useState

作用:

复制代码
给函数组件定义状态

例如:

javascript 复制代码
import { useState } from 'react'

function App() {

  const [count, setCount] =
    useState(0)

  return (
    <button
      onClick={() =>
        setCount(count + 1)
      }
    >
      {count}
    </button>
  )
}

useState 结构

scss 复制代码
const [state, setState] =
  useState(initialValue)

例如:

scss 复制代码
const [count, setCount] =
  useState(0)

对应:

复制代码
count
↓
当前状态

setCount
↓
修改状态

useEffect

作用:

复制代码
处理副作用

例如:

复制代码
发送请求

操作DOM

监听事件

定时器

这些都属于副作用。


useEffect 默认执行时机

scss 复制代码
useEffect(() => {

})

触发:

diff 复制代码
首次渲染

+

每次重新渲染

只执行一次

scss 复制代码
useEffect(() => {

}, [])

触发:

复制代码
仅组件首次加载

等价于:

复制代码
componentDidMount

监听某个变量

scss 复制代码
useEffect(() => {

}, [count])

触发:

diff 复制代码
首次渲染

+

count变化时

例如:

复制代码
count更新

↓

useEffect执行

useEffect 返回函数

scss 复制代码
useEffect(() => {

  return () => {

  }

}, [])

返回函数称为:

javascript 复制代码
Cleanup Function
清理函数

执行时机:

复制代码
组件卸载前

例如:

scss 复制代码
useEffect(() => {

  const timer =
    setInterval(() => {},1000)

  return () => {

    clearInterval(timer)

  }

}, [])

作用:

复制代码
释放资源

避免内存泄漏

生命周期与 Hooks 对应关系

类组件:

复制代码
componentDidMount

componentDidUpdate

componentWillUnmount

函数组件:

scss 复制代码
useEffect(() => {

  // mount

  return () => {

    // unmount

  }

}, [])

再配合依赖数组:

scss 复制代码
useEffect(() => {

  // update

}, [state])

基本覆盖生命周期需求。


为什么 Hooks 更优秀?

1. 没有 this

类组件:

kotlin 复制代码
经常出现this指向问题

函数组件:

kotlin 复制代码
没有this

逻辑更简单。


2. 更容易复用逻辑

多个组件共享逻辑:

复制代码
自定义Hook

即可实现。


3. 代码更集中

类组件:

复制代码
一个功能

分散在多个生命周期

Hooks:

复制代码
一个功能

集中在一个useEffect

维护成本更低。


面试高频题

什么是 State?

复制代码
状态

数据变化会导致组件重新渲染

类组件如何定义状态?

kotlin 复制代码
this.state

如何修改状态?

kotlin 复制代码
this.setState()

useState 的作用?

复制代码
为函数组件定义状态

useEffect 默认什么时候执行?

diff 复制代码
首次渲染

+

每次重新渲染

useEffect(\[\]) 什么时候执行?

复制代码
仅首次加载

useEffect(x) 什么时候执行?

diff 复制代码
首次加载

+

x变化时

useEffect 返回函数有什么作用?

复制代码
组件卸载前执行

用于资源清理

一张图记忆

markdown 复制代码
React

│

├── React16.8之前
│
│    └── 类组件
│          │
│          ├── state
│          ├── setState
│          └── 生命周期
│
└── React16.8之后
      │
      └── Hooks
            │
            ├── useState
            └── useEffect

生命周期

componentDidMount
        ↓

componentDidUpdate
        ↓

componentWillUnmount

对应

useEffect

核心口诀

diff 复制代码
React16.8之前:

类组件

state

setState

生命周期

----------------

React16.8之后:

函数组件

Hooks

----------------

useState:

定义状态

setState:

修改状态

----------------

useEffect():

首次加载
+
每次更新

useEffect([],):

仅首次加载

useEffect([x]):

首次加载
+
x变化

----------------

return函数:

组件卸载前执行
用于清理资源
相关推荐
weedsfly1 小时前
防抖与节流:从原理到 React Hook 封装
前端·javascript·面试
天蓝色的鱼鱼1 小时前
让 AI 写代码半年后,我发现了几个藏在项目里的安全隐患
前端·javascript·ai编程
随风一样自由1 小时前
【前端+跨端技术新格局】Tauri vs Electron 完整性能对比(2026实测数据+底层原理)
前端·javascript·electron
L-影1 小时前
Pydantic 模型:为你的 Web 数据装上“安检系统”
前端
Reload.2 小时前
GJ航司,验证码网易易盾 JS逆向 纯算轨迹
开发语言·前端·javascript
L-影2 小时前
FastAPI 请求头与 Cookie:Web 世界的“身份证”与“通行证”
前端·fastapi
IT_陈寒2 小时前
Vue的响应式更新把我坑惨了,原来是这个原因
前端·人工智能·后端
铁皮饭盒2 小时前
Bun.js 集成了 SQL/Redis/S3,为什么 Java/Go/Python 不集成?
javascript
年轻的砖头2 小时前
以Ajax方式提交整个表单
前端·javascript·ajax