零基础也能懂!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,调用后会触发组件重新渲染
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个必用)
类组件通过固定的生命周期钩子函数,处理组件「挂载、更新、卸载」三个核心阶段的业务逻辑,也是类组件处理副作用(请求数据、定时器、事件监听)的唯一方式:
-
componentDidMount:组件初次挂载完成后执行一次 → 常用:发起异步请求、绑定事件监听、开启定时器 -
componentDidUpdate:组件每次状态更新渲染完成后执行 → 常用:根据状态变化更新DOM、发起关联请求 -
componentWillUnmount:组件即将卸载销毁前执行一次 → 常用:清除定时器、解绑事件监听、取消请求,做收尾清理工作
二、React16.8+ 新时代:函数组件 + Hooks 封神(全文重点)
React 团队推出 Hooks 的核心目的:为函数组件赋能,让原本「无状态、无生命周期」的函数组件,拥有和类组件同等的能力,同时解决类组件 this 指向混乱、生命周期逻辑分散、复用状态逻辑繁琐的痛点。
✨ 核心结论
-
React17+ 项目开发,优先使用【函数组件 + Hooks】 是绝对主流
-
Hooks 翻译为「钩子」,本质是:为函数组件提供的一系列内置函数,让函数组件拥有状态、生命周期、副作用管理能力
-
核心核心2个基础钩子:
useState(状态管理) +useEffect(生命周期+副作用管理),掌握这2个,就能完成90%的业务开发!
三、核心Hook ①:useState — 函数组件的「状态管理神器」
✅ 作用
为函数组件定义响应式状态,完美替代类组件的 this.state + this.setState,是函数组件能拥有「自身状态」的核心。
✅ 语法格式
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)
✅ 基础使用(替代类组件状态)
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. 复杂状态初始化(函数式初始化)
如果状态的初始值需要复杂计算、异步获取、耗时操作,直接写值会导致组件每次渲染都执行该计算,性能浪费。此时传入一个函数,该函数只会在组件初次挂载时执行一次,返回初始值。
// 推荐:复杂初始化用函数,只执行一次
const [name, setName] = useState(() => {
return '张三' // 可写任意复杂逻辑
})
2. 依赖原状态更新(函数式更新)
当新状态的取值依赖于上一次的旧状态时,推荐给更新函数传入一个回调函数,回调函数的参数就是「最新的旧状态」,确保拿到的状态值永远是最新的,避免异步更新导致的取值错误。
const add = () => {
// 函数式更新:prevCount 是最新的旧状态
setCount(prevCount => prevCount + 1)
}
// 数组/对象同理
const [list, setList] = useState([])
const pushItem = () => {
setList(prevList => [...prevList, '新数据'])
}
✅ useState 核心优势
-
一个组件中可以多次调用,定义多个独立状态,互不影响,状态管理更灵活
-
无需处理
this指向问题,类组件的this.setState经常需要绑定this,Hooks 完全规避 -
写法极简,无冗余模板代码,代码量比类组件减少50%以上
四、核心Hook ②:useEffect — 函数组件的「生命周期+副作用万能钩子」
✅ 核心定义
useEffect 是函数组件中处理 副作用 的唯一入口,同时完美替代类组件的3个核心生命周期,是函数组件的重中之重。
副作用:指和组件渲染无关的操作,比如:异步请求、开启/清除定时器、绑定/解绑事件监听、操作DOM、本地存储等。
✅ 核心语法
import { useEffect, useState } from 'react' // 配套导入
useEffect(() => {
// 【核心执行体】:需要执行的副作用逻辑
console.log('执行副作用')
// 【清理函数】:可选,return 一个函数,组件卸载时执行
return () => {
console.log('组件卸载,执行清理工作')
}
}, [依赖项数组]) // 核心:通过依赖项控制执行时机
✅ 关键规则(重中之重,必背)
useEffect 的执行时机,完全由第二个参数【依赖项数组】决定,这也是和类组件生命周期一一对应的核心,4种核心用法覆盖所有业务场景,对应类组件的生命周期精准无差:
1. 无依赖项数组 → 等价于 类组件 componentDidMount + componentDidUpdate
useEffect(() => {
console.log('组件初次加载执行 + 每次渲染更新后都执行')
})
组件初次挂载完成后执行一次,之后每次组件重新渲染(任意状态变化)都会再次执行,适合需要「每次更新都同步执行」的逻辑。
2. 空依赖项数组 [] → 等价于 类组件 componentDidMount
useEffect(() => {
console.log('只在组件【初次挂载】时执行一次,永久不重复执行')
// 【最常用场景】:发起初始化异步请求、开启定时器、绑定全局事件
}, [])
✅ 核心高频用法:组件加载完成后只请求一次接口,React开发中80%的接口请求都用这种写法!
useEffect(() => {
// 初始化请求数据
fetch('接口地址')
.then(res => res.json())
.then(data => {
console.log('请求成功', data)
})
}, [])
3. 依赖项数组带指定变量 [x,y] → 等价于 类组件 componentDidUpdate
const [count, setCount] = useState(0)
// 依赖 count 变量
useEffect(() => {
console.log('组件初次加载执行一次 + 每次 count 变化时执行一次')
// 场景:根据某个状态的变化,做关联逻辑(比如:搜索关键词变化重新请求接口)
}, [count])
精准监听指定状态,只有依赖的变量发生改变时,才会执行副作用逻辑,是性能优化的核心写法,也是类组件 componentDidUpdate 的精准平替。
4. useEffect 返回清理函数 → 等价于 类组件 componentWillUnmount
useEffect(() => {
// 挂载时:开启定时器
const timer = setInterval(() => {
setCount(prev => prev + 1)
}, 1000)
// 卸载时:执行return的清理函数 → 清除定时器
return () => {
clearInterval(timer)
}
}, [])
✅ 核心规则:return 的回调函数,会在组件即将卸载销毁前 执行一次,专门用来做「清理工作」,比如清除定时器、解绑事件监听、取消未完成的请求,和类组件 componentWillUnmount 功能完全一致,且写法更优雅,副作用和清理逻辑写在同一个地方,不会遗漏。
五、类组件 VS 函数组件+Hooks 实战对比(核心精华)
✅ 核心结论:写法对比,差距一目了然
同样实现「计数器+定时器」功能,类组件,函数组件+Hooks,代码量、可读性、简洁度 完胜!
类组件实现(繁琐、冗余、有this问题)
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实现(极简、优雅、无冗余)
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 核心优势(为什么成为主流?)
-
this彻底抛弃 关键字:类组件的最大痛点就是this指向混乱,需要 bind、箭头函数等方式处理,Hooks 完全规避,代码更干净。 -
逻辑更聚合:类组件中,一个业务的「初始化+清理」逻辑会分散在
componentDidMount和componentWillUnmount两个生命周期中,Hooks 中写在同一个useEffect里,逻辑闭环,可读性拉满。 -
无冗余模板代码:无需写
class、constructor、render,一个函数搞定所有逻辑,代码量直接减少一半以上。 -
状态复用更简单:Hooks 可以轻松抽离公共逻辑(比如请求封装、表单处理),实现跨组件复用,类组件的复用需要高阶组件/HOC,写法繁琐。
-
官方主推方向:React 团队明确表示,未来的新特性都会优先支持 Hooks,类组件不再新增特性,仅做兼容维护。
六、必避的2个 Hooks 常见坑(新手必看)
坑1:直接修改 useState 的状态值,不调用更新函数
// ❌ 错误:直接修改数组/对象,不会触发组件重新渲染
const [list, setList] = useState([])
const add = () => {
list.push('test')
}
// ✅ 正确:必须调用更新函数,返回新的状态值
const add = () => {
setList([...list, 'test'])
}
核心原理:React 的状态是「不可变的」,只有通过更新函数返回新值,React 才能检测到状态变化,触发渲染。
坑2:useEffect 依赖项数组「漏写/错写」
// ❌ 错误:使用了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,搞定所有开发需求
-
useState:给函数组件加「状态」,替代类组件的
this.state + setState,无 this 烦恼,写法极简。 -
useEffect:给函数组件加「生命周期+副作用」,一个钩子顶类组件3个生命周期,逻辑聚合,是开发核心。
3. 终极选型建议
-
新项目开发:无脑用 函数组件 + Hooks,这是React的未来,也是企业招聘的主流要求。
-
维护老项目:类组件的知识足够看懂即可,无需新增类组件代码。
-
核心心法:Hooks的本质是「为函数组件赋能」,让函数组件拥有类组件的所有能力,同时解决类组件的痛点。 ✨