零基础也能懂!React Hooks实战手册:useState/useEffect上手就会,告别类组件

React16.8 之前,函数组件只能作为「无状态组件」存在,所有需要状态管理、生命周期处理的业务需求,都只能依赖类组件实现。而 React16.8 推出 Hooks 特性后,彻底颠覆了函数组件的能力边界,让函数组件可以优雅的实现状态、生命周期、副作用管理等所有功能,也让函数组件成为 React 官方主推、企业开发的主流选型。

本文将极简回顾类组件核心用法,重点精讲 React Hooks 的使用、原理和实战技巧,吃透这篇,彻底掌握 React16.8+ 的主流开发方式。

一、React16.8 之前:类组件一统天下

在 Hooks 出现之前,想要开发带「状态」的 React 组件,类组件是唯一选择,核心能力只有两个:状态管理 + 生命周期钩子。

✅ 类组件 核心2大能力

1. 组件状态管理

  • 初始化状态:通过 this.state = { } 定义组件自身的响应式状态(状态:修改后会触发组件重新渲染的变量)

  • 更新状态:this.setState() 必须通过 方法修改状态 ,禁止直接赋值修改 this.state,调用后会触发组件重新渲染

jsx 复制代码
import React, { Component } from 'react'
// 类组件核心示例(含状态管理+生命周期)
class App extends Component {
  constructor() {
    super()
    // 初始化响应式状态
    this.state = { count: 0 }
  }

  // 组件首次加载完成后执行(初始化请求/定时器常用)
  componentDidMount() {
    console.log('组件加载完毕');
  }

  // 状态更新后执行
  componentDidUpdate() {
    console.log('组件更新完毕');
  }

  // 状态更新方法:必须通过setState修改状态
  add() {
    this.setState({ count: this.state.count + 1 })
  }

  render() {
    return (
      <div>
        <h2>{this.state.count}</h2>
        {/* 绑定事件需通过bind确保this指向 */}
        <button onClick={this.add.bind(this)}>add</button>
      </div>
    )
  }
}
export default App

2. 类组件核心生命周期(3个必用)

类组件通过固定的生命周期钩子函数,处理组件「挂载、更新、卸载」三个核心阶段的业务逻辑,也是类组件处理副作用(请求数据、定时器、事件监听)的唯一方式:

  1. componentDidMount:组件初次挂载完成后执行一次 → 常用:发起异步请求、绑定事件监听、开启定时器

  2. componentDidUpdate:组件每次状态更新渲染完成后执行 → 常用:根据状态变化更新DOM、发起关联请求

  3. componentWillUnmount:组件即将卸载销毁前执行一次 → 常用:清除定时器、解绑事件监听、取消请求,做收尾清理工作


二、React16.8+ 新时代:函数组件 + Hooks 封神(全文重点)

React 团队推出 Hooks 的核心目的:为函数组件赋能 ,让原本「无状态、无生命周期」的函数组件,拥有和类组件同等的能力,同时解决类组件 this 指向混乱、生命周期逻辑分散、复用状态逻辑繁琐的痛点。

✨ 核心结论

  1. React17+ 项目开发,优先使用【函数组件 + Hooks】 是绝对主流

  2. Hooks 翻译为「钩子」,本质是:为函数组件提供的一系列内置函数,让函数组件拥有状态、生命周期、副作用管理能力

  3. 核心核心2个基础钩子:useState(状态管理) + useEffect(生命周期+副作用管理),掌握这2个,就能完成90%的业务开发!

三、核心Hook ①:useState --- 函数组件的「状态管理神器」

✅ 作用

函数组件定义响应式状态 ,完美替代类组件的 this.state + this.setState,是函数组件能拥有「自身状态」的核心。

✅ 语法格式

jsx 复制代码
import { useState } from 'react' // 必须手动导入

// 语法:const [状态变量, 状态更新函数] = useState(初始值)
const [count, setCount] = useState(0)
const [list, setList] = useState([])
const [userInfo, setUserInfo] = useState({name: '张三', age: 20})
  • 数组解构赋值,变量名可自定义,语义化命名即可

  • 第一个参数:状态变量 ,直接使用即可,无需 this

  • 第二个参数:状态更新函数 ,调用该函数修改状态,会触发组件重新渲染

  • 括号内:状态初始值,可以是任意类型(数字、数组、对象、null/undefined)

✅ 基础使用(替代类组件状态)

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

export function Counter() {
  // 定义状态:计数初始值为0
  const [count, setCount] = useState(0)

  // 修改状态:调用setCount,直接更新,无this、无绑定
  const add = () => {
    setCount(count + 1)
  }

  return (
    <div>
      <h2>计数:{count}</h2>
      <button onClick={add}>add</button>//点击加一
    </div>
  )
}

✅ 进阶用法

1. 复杂状态初始化(函数式初始化)

如果状态的初始值需要复杂计算、异步获取、耗时操作 ,直接写值会导致组件每次渲染都执行该计算,性能浪费。此时传入一个函数 ,该函数只会在组件初次挂载时执行一次,返回初始值。

jsx 复制代码
// 推荐:复杂初始化用函数,只执行一次
const [name, setName] = useState(() => {
  return '张三' // 可写任意复杂逻辑
})

2. 依赖原状态更新(函数式更新)

当新状态的取值依赖于上一次的旧状态时,推荐给更新函数传入一个回调函数,回调函数的参数就是「最新的旧状态」,确保拿到的状态值永远是最新的,避免异步更新导致的取值错误。

jsx 复制代码
const add = () => {
  // 函数式更新:prevCount 是最新的旧状态
  setCount(prevCount => prevCount + 1)
}
// 数组/对象同理
const [list, setList] = useState([])
const pushItem = () => {
  setList(prevList => [...prevList, '新数据'])
}

✅ useState 核心优势

  1. 一个组件中可以多次调用,定义多个独立状态,互不影响,状态管理更灵活

  2. 无需处理 this 指向问题,类组件的 this.setState 经常需要绑定 this,Hooks 完全规避

  3. 写法极简,无冗余模板代码,代码量比类组件减少50%以上


四、核心Hook ②:useEffect --- 函数组件的「生命周期+副作用万能钩子」

✅ 核心定义

useEffect 是函数组件中处理 副作用 的唯一入口,同时完美替代类组件的3个核心生命周期,是函数组件的重中之重。

副作用:指和组件渲染无关的操作,比如:异步请求、开启/清除定时器、绑定/解绑事件监听、操作DOM、本地存储等。

✅ 核心语法

jsx 复制代码
import { useEffect, useState } from 'react' // 配套导入

useEffect(() => {
  // 【核心执行体】:需要执行的副作用逻辑
  console.log('执行副作用')
  
  // 【清理函数】:可选,return 一个函数,组件卸载时执行
  return () => {
    console.log('组件卸载,执行清理工作')
  }
}, [依赖项数组]) // 核心:通过依赖项控制执行时机

✅ 关键规则(重中之重,必背)

useEffect 的执行时机,完全由第二个参数【依赖项数组】决定,这也是和类组件生命周期一一对应的核心,4种核心用法覆盖所有业务场景,对应类组件的生命周期精准无差:

1. 无依赖项数组 → 等价于 类组件 componentDidMount + componentDidUpdate

jsx 复制代码
useEffect(() => {
  console.log('组件初次加载执行 + 每次渲染更新后都执行')
})

组件初次挂载完成后执行一次 ,之后每次组件重新渲染(任意状态变化)都会再次执行,适合需要「每次更新都同步执行」的逻辑。

2. 空依赖项数组 [] → 等价于 类组件 componentDidMount

jsx 复制代码
useEffect(() => {
  console.log('只在组件【初次挂载】时执行一次,永久不重复执行')
  // 【最常用场景】:发起初始化异步请求、开启定时器、绑定全局事件
}, [])

✅ 核心高频用法:组件加载完成后只请求一次接口,React开发中80%的接口请求都用这种写法!

jsx 复制代码
useEffect(() => {
  // 初始化请求数据
  fetch('接口地址')
    .then(res => res.json())
    .then(data => {
      console.log('请求成功', data)
    })
}, [])

3. 依赖项数组带指定变量 [x,y] → 等价于 类组件 componentDidUpdate

jsx 复制代码
const [count, setCount] = useState(0)
// 依赖 count 变量
useEffect(() => {
  console.log('组件初次加载执行一次 + 每次 count 变化时执行一次')
  // 场景:根据某个状态的变化,做关联逻辑(比如:搜索关键词变化重新请求接口)
}, [count])

精准监听指定状态,只有依赖的变量发生改变 时,才会执行副作用逻辑,是性能优化的核心写法,也是类组件 componentDidUpdate 的精准平替。

4. useEffect 返回清理函数 → 等价于 类组件 componentWillUnmount

jsx 复制代码
useEffect(() => {
  // 挂载时:开启定时器
  const timer = setInterval(() => {
    setCount(prev => prev + 1)
  }, 1000)
  
  // 卸载时:执行return的清理函数 → 清除定时器
  return () => {
    clearInterval(timer)
  }
}, [])

✅ 核心规则:return 的回调函数,会在组件即将卸载销毁前 执行一次,专门用来做「清理工作」,比如清除定时器、解绑事件监听、取消未完成的请求,和类组件 componentWillUnmount 功能完全一致,且写法更优雅,副作用和清理逻辑写在同一个地方,不会遗漏。


五、类组件 VS 函数组件+Hooks 实战对比(核心精华)

✅ 核心结论:写法对比,差距一目了然

同样实现「计数器+定时器」功能,类组件,函数组件+Hooks,代码量、可读性、简洁度 完胜!

类组件实现(繁琐、冗余、有this问题)

jsx 复制代码
import React, { Component } from 'react'
class App extends Component {
  state = { count: 0 }
  timer = null

  componentDidMount() {
    // 挂载开启定时器
    this.timer = setInterval(() => {
      this.setState({ count: this.state.count + 1 })
    }, 1000)
  }

  componentWillUnmount() {
    // 卸载清除定时器
    clearInterval(this.timer)
  }

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

函数组件+Hooks实现(极简、优雅、无冗余)

jsx 复制代码
import { useState, useEffect } from 'react'
export function App() {
  const [count, setCount] = useState(0)
  
  useEffect(() => {
    const timer = setInterval(() => {
      setCount(prev => prev + 1)
    }, 1000)
    return () => clearInterval(timer)
  }, [])

  return <h2>{count}</h2>
}

✅ Hooks 核心优势(为什么成为主流?)

  1. this 彻底抛弃 关键字 :类组件的最大痛点就是this 指向混乱,需要 bind、箭头函数等方式处理,Hooks 完全规避,代码更干净。

  2. 逻辑更聚合 :类组件中,一个业务的「初始化+清理」逻辑会分散在 componentDidMountcomponentWillUnmount 两个生命周期中,Hooks 中写在同一个 useEffect 里,逻辑闭环,可读性拉满。

  3. 无冗余模板代码 :无需写 classconstructorrender,一个函数搞定所有逻辑,代码量直接减少一半以上。

  4. 状态复用更简单:Hooks 可以轻松抽离公共逻辑(比如请求封装、表单处理),实现跨组件复用,类组件的复用需要高阶组件/HOC,写法繁琐。

  5. 官方主推方向:React 团队明确表示,未来的新特性都会优先支持 Hooks,类组件不再新增特性,仅做兼容维护。


六、必避的2个 Hooks 常见坑(新手必看)

坑1:直接修改 useState 的状态值,不调用更新函数

jsx 复制代码
// ❌ 错误:直接修改数组/对象,不会触发组件重新渲染
const [list, setList] = useState([])
const add = () => {
  list.push('test')
}

// ✅ 正确:必须调用更新函数,返回新的状态值
const add = () => {
  setList([...list, 'test'])
}

核心原理:React 的状态是「不可变的」,只有通过更新函数返回新值,React 才能检测到状态变化,触发渲染。

坑2:useEffect 依赖项数组「漏写/错写」

jsx 复制代码
// ❌ 错误:使用了count变量,但依赖项数组中没写,导致拿到的count永远是初始值
useEffect(() => {
  setInterval(() => {
    setCount(count + 1)
  }, 1000)
}, [])

// ✅ 正确:要么补全依赖项,要么用函数式更新
useEffect(() => {
  setInterval(() => {
    setCount(prev => prev + 1)
  }, 1000)
}, [])

七、总结(精炼核心,直击重点)

1. 版本分界清晰

  • React16.8 前:类组件是唯一能写「完整功能」的组件,靠this.state/setState 管理状态,靠3个生命周期处理副作用。

  • React16.8+:函数组件 + Hooks 成为绝对主流,类组件能做的,函数组件都能做,且做得更好。

2. 核心2个Hook,搞定所有开发需求

  1. useState :给函数组件加「状态」,替代类组件的 this.state + setState,无 this 烦恼,写法极简。

  2. useEffect:给函数组件加「生命周期+副作用」,一个钩子顶类组件3个生命周期,逻辑聚合,是开发核心。

3. 终极选型建议

  • 新项目开发:无脑用 函数组件 + Hooks,这是React的未来,也是企业招聘的主流要求。

  • 维护老项目:类组件的知识足够看懂即可,无需新增类组件代码。

  • 核心心法:Hooks的本质是「为函数组件赋能」,让函数组件拥有类组件的所有能力,同时解决类组件的痛点。 ✨

相关推荐
xhxxx2 小时前
从样式到结构:TailwindCss + Fragment 如何让 React 代码更干净、更高效
前端·css·react.js
Maxkim2 小时前
「✍️JS原子笔记 」深入理解JS数据类型检测的4种核心方式
前端·javascript·面试
小高0072 小时前
Elips-Core:轻量级 Node.js Web 框架核心实现
前端·javascript·node.js
Focus_2 小时前
SSE+broadcastChannel
前端
zabr2 小时前
前端已死?我用 Trae + Gemini 零代码手搓 3D 塔罗牌,找到了新出路
前端·人工智能·aigc
Aotman_2 小时前
Vue MutationObserver 监听
前端·javascript·vue.js·elementui·前端框架·ecmascript
专注前端30年2 小时前
Vue3的生命周期钩子有哪些变化?
前端·javascript·vue.js
ICT技术最前线2 小时前
企业 ICT 标准化传输架构规范
架构
踏浪无痕3 小时前
JobFlow:时间轮与滑动窗口的实战优化
后端·架构·开源