React Hooks 基础入门:从“懵圈”到“真香”
一个 2019 年入行、被 Hooks 整哭过又被 Hooks 救活的前端,写给你的可阅读可理解的入门指南
先讲个故事:我为什么讨厌(又喜欢)Hooks
2019 年,我还在大厂外包项目里写 React。那时候的 React 还是 class 组件一统天下。每个组件都要写constructor、super(props)、this.state = {...}、this.handleClick = this.handleClick.bind(this)……光这些模板代码,还没写业务逻辑,20 行就出去了。
后来 React 16.8 发布了 Hooks。我当时的反应是:又来新东西?不想学。
直到有一次,我写了一个计时器组件。用 class 组件写,componentDidMount里开启定时器,componentWillUnmount里清理。核心逻辑就几行,但是要分散在两个生命周期里。再后来,组件里加了订阅窗口大小、请求数据、手动修改 DOM……各种逻辑混在一起,一个组件几百行,看得我自己都头皮发麻。
我组长说:“你试试 Hooks 吧。”
我硬着头皮学了一周。刚开始确实懵——为什么useState返回数组?为什么useEffect依赖数组写错了会无限循环?为什么自定义 Hook 名字必须以use开头?但是当我真正写出来第一个用 Hooks 重构的组件时,我惊呼:原来代码可以这么清爽!
这篇文章,就是我把当年踩过的坑、翻过的文档、问过的“蠢问题”,全部翻译成可阅读可理解的,送给你。
第一部分:Hooks 到底是什么?为什么 React 要搞出这个东西?
1.1 官方定义
React Hooks 是 React 16.8 新增的一组函数,让你在函数组件里使用 state 和生命周期能力。
在 Hooks 出现之前,函数组件只能做“纯展示”——接收 props,返回 UI,不能有状态,不能有副作用。要想有状态,必须写成 class 组件。
Hooks 出现以后,函数组件“开挂”了:你可以用useState加状态,用useEffect处理副作用,用useContext跨层级传数据……函数组件从此无所不能。
1.2 为什么要有 Hooks?
痛点一:class 组件的逻辑复用太难了
在 class 组件年代,如果你想复用一段带状态的逻辑(比如监听窗口宽度、订阅鼠标位置),你需要用高阶组件(HOC)或render props。这两种模式都需要额外的组件包装,形成“嵌套地狱”:
<WithMouse> {mouse => ( <WithWindowSize> {size => ( <MyComponent mouse={mouse} size={size} /> )} </WithWindowSize> )} </WithMouse>这叫“封装金字塔”,读代码的人得钻进钻出,改代码的人想骂娘。
Hooks 让你把逻辑抽成自定义 Hook,直接调用,没有嵌套。
痛点二:class 组件里的逻辑分散在各个生命周期
一个典型的 class 组件,数据请求、事件订阅、手动 DOM 操作可能散落在componentDidMount、componentDidUpdate、componentWillUnmount里。你想关注“请求数据”这条线,得在三个生命周期里来回跳转。
Hooks 让你按“功能”组织代码,而不是按“生命周期”。同一个功能的逻辑可以写在一个useEffect里,甚至抽成自定义 Hook。
痛点三:class 的 this 和行为难以理解
JavaScript 的this机制让很多初学者(甚至老手)头疼。你需要手动.bind(this),或者用箭头函数,否则事件处理函数里的this就是undefined。Hooks 在函数组件里,没有this,全靠闭包,心智负担直接降为零。
1.3 Hooks 的两条黄金规则
在你继续往下看之前,先把这两条规则刻在脑门上:
- 只在最顶层使用 Hooks:不要在循环、条件判断或嵌套函数里调用
useState、useEffect等。因为 React 靠调用顺序来区分每个 Hook 对应的状态。 - 只在 React 函数组件或自定义 Hook 里调用 Hooks:不要在普通的 JS 函数里用。
违反任何一条,你的代码就会神秘崩溃。别问我怎么知道的。
第二部分:最常用的 Hooks —— 先学会这 5 个,能写 90% 场景
2.1 useState —— 让函数组件拥有“记忆”
解释:useState相当于给函数组件加了一个“记忆盒子”。盒子里的东西变了,组件就会重新渲染,显示新的值。
基本用法:
import { useState } from 'react' function Counter() { // 声明一个叫 count 的状态变量,初始值为 0 // setCount 是用来修改 count 的函数 const [count, setCount] = useState(0) function handleClick() { setCount(count + 1)