一、什么是 React HookReact Hook本质上是一套让函数组件拥有更多能力的机制。你可以先记住一句最核心的话Hook 的作用就是让函数组件也能拥有状态、生命周期、副作用处理、逻辑复用等能力。通俗一点说以前 React 的函数组件只会干一件事根据数据把页面渲染出来。但是现实开发中一个组件往往不只是“显示页面”这么简单它还需要做很多事情比如记住用户输入的内容控制弹窗开关发送接口请求监听页面变化获取 DOM 元素复用一段公共逻辑而 Hook就是 React 提供给函数组件的一套“能力插件”。你可以把它理解成Hook 给函数组件装功能的工具箱。二、为什么会有 Hook要理解 Hook先得知道 React 以前是怎么写的。在 Hook 出现之前React 中如果你想让组件拥有状态、生命周期这些能力通常要使用类组件Class Component。比如一个最简单的计数器早期可能要这样写import React, { Component } from react; class Counter extends Component { constructor(props) { super(props); this.state { count: 0 }; } render() { return ( button onClick{() this.setState({ count: this.state.count 1 })} 点击了 {this.state.count} 次 /button ); } }这段代码没有错但很多初学者会觉得有点麻烦要写class要写constructor要写super要写this.state要写this.setStatethis指向有时候还容易出问题随着项目越来越复杂类组件还会涉及各种生命周期函数比如componentDidMountcomponentDidUpdatecomponentWillUnmount代码会越来越分散逻辑也越来越不容易维护。于是 React 官方推出了 Hook。它的目标非常明确让函数组件也能完成类组件的大部分能力而且写起来更简洁、更清晰、更容易复用逻辑。三、你可以怎么理解 Hook你可以把一个 React 组件想象成一个员工。一开始这个员工只会一件事把页面画出来。比如function Hello() { return h1Hello React/h1; }这就是一个最普通的函数组件。但如果你想让这个员工更能干一点比如记住一个数字页面加载后发请求监听窗口变化找到某个输入框并让它自动获取焦点那就需要给他配工具。而 Hook就是这些工具。比如useState给员工一个记事本让他能记住东西useEffect给员工一个任务清单让他在页面渲染后做额外事情useRef给员工一个抽屉可以放东西也能找到页面里的某个元素所以Hook 并不神秘。它就是让函数组件从“只能展示页面”升级成“能真正干活的组件”。四、最常见的 HookuseState在 React 中最常用的 Hook 之一就是useState。它的作用非常简单让组件记住一个值。比如我们写一个最基础的计数器import React, { useState } from react; function Counter() { const [count, setCount] useState(0); return ( button onClick{() setCount(count 1)} 点击了 {count} 次 /button ); } export default Counter;这段代码怎么理解最关键的是这句const [count, setCount] useState(0);你可以把它翻译成一句人话React帮我准备一个状态它的初始值是 0当前值叫 count修改它的方法叫 setCount。于是count是当前的状态值setCount是修改状态的方法useState(0)里的0是初始值点击按钮后执行setCount(count 1);React 就会更新状态然后重新渲染页面。为什么不能直接用普通变量很多初学者可能会写出这样的代码function Counter() { let count 0; return ( button onClick{() count} 点击了 {count} 次 /button ); }看起来你在修改count但页面并不会按预期更新。原因很简单普通变量的变化React 感知不到。React 只会对“状态”的变化做出响应而useState正是告诉 React这个值是组件状态请你帮我管理它。所以你可以这样记普通变量是你自己偷偷记React 不知道useState 是你正式告诉 React这个值要参与页面更新。五、第二个非常重要的 HookuseEffect除了状态组件还经常需要做一些“额外的事情”。比如页面加载后请求接口设置定时器监听滚动事件修改页面标题组件销毁时做清理这些事情并不是“渲染 UI”本身而是渲染之外的行为。React 把这类操作叫做副作用Effect。听起来有点专业其实你完全可以把它理解成组件渲染完后顺手做的事。这时候就要用到useEffect。先看一个例子import React, { useEffect } from react; function Demo() { useEffect(() { console.log(组件渲染完成了); }, []); return div你好React Hook/div; } export default Demo;这段代码的意思就是页面渲染出来以后执行一次里面的代码。[]是什么意思很多人第一次学useEffect最容易懵的地方就是第二个参数。useEffect(() { console.log(执行了); }, []);这里的[]叫做依赖数组。它决定这个副作用什么时候执行。1. 传空数组[]useEffect(() { console.log(只执行一次); }, []);表示组件第一次渲染完成后执行一次以后不再执行。这很像类组件里的componentDidMount。2. 不传第二个参数useEffect(() { console.log(每次渲染都执行); });表示组件每次渲染后都会执行。3. 传入依赖项useEffect(() { console.log(count 变化了); }, [count]);表示首次渲染会执行以后只有 count 变化时才执行。这个机制非常重要写业务代码时经常会用到。六、useEffect 最经典的应用请求接口比如页面加载后获取用户列表import React, { useEffect, useState } from react; function UserList() { const [users, setUsers] useState([]); useEffect(() { fetch(https://jsonplaceholder.typicode.com/users) .then((res) res.json()) .then((data) { setUsers(data); }); }, []); return ( div h2用户列表/h2 ul {users.map((user) ( li key{user.id}{user.name}/li ))} /ul /div ); } export default UserList;逻辑很清晰用useState([])准备一个用户列表状态用useEffect在组件第一次渲染后发请求请求回来后通过setUsers(data)更新状态页面自动重新渲染显示数据这就是 Hook 配合使用的典型场景。七、第三个常见 HookuseRef接下来再说一个很常用的 HookuseRef。它主要有两个用途获取 DOM 元素保存一个不会触发页面重新渲染的值先看第一个用途获取 DOM。比如页面加载后让输入框自动聚焦import React, { useEffect, useRef } from react; function InputFocus() { const inputRef useRef(null); useEffect(() { inputRef.current.focus(); }, []); return input ref{inputRef} placeholder请输入内容 /; } export default InputFocus;这里的逻辑是useRef(null)创建一个引用对象ref{inputRef}把这个引用绑定到 input 上页面渲染后通过inputRef.current拿到真实 DOM调用focus()让输入框自动获取焦点你可以把useRef理解成给页面元素贴了个标签方便以后找到它。useRef 的第二个用途useRef还可以用来保存一些值而且这些值变化时不会导致页面重新渲染。比如保存一个定时器 idimport React, { useEffect, useRef } from react; function TimerDemo() { const timerRef useRef(null); useEffect(() { timerRef.current setInterval(() { console.log(定时器执行中); }, 1000); return () { clearInterval(timerRef.current); }; }, []); return div定时器示例/div; } export default TimerDemo;这里的timerRef.current就像一个小盒子可以存放数据。和useState的区别是useState变化会触发重新渲染useRef变化不会触发重新渲染所以如果你只是想保存一个值但这个值不需要展示到页面上useRef很合适。八、React Hook 到底解决了什么问题这是很多面试中也会问到的问题。React Hook 主要解决了三个问题。1. 让函数组件拥有状态和副作用能力以前函数组件只能负责展示 UI复杂逻辑很多都要写在类组件里。Hook 出现后函数组件也能做这些事了开发体验更统一。2. 逻辑复用更方便以前如果你想复用一段组件逻辑常见方法有mixin高阶组件HOCRender Props这些方案不是不能用但随着项目变复杂代码嵌套会越来越深理解成本也越来越高。而 Hook 可以把一段逻辑直接提取成一个自定义 Hook。比如获取窗口宽度import React, { useEffect, useState } from react; function useWindowWidth() { const [width, setWidth] useState(window.innerWidth); useEffect(() { const handleResize () { setWidth(window.innerWidth); }; window.addEventListener(resize, handleResize); return () { window.removeEventListener(resize, handleResize); }; }, []); return width; } function Page() { const width useWindowWidth(); return div当前窗口宽度{width}/div; } export default Page;这里useWindowWidth()就是一个自定义 Hook。你会发现这种写法真的很舒服逻辑抽离清晰复用方便代码结构更自然3. 让组件代码更简洁、更易维护类组件中一个功能的相关代码可能分散在多个生命周期里。而用 Hook可以把“相关逻辑”写在一起。比如接口请求、事件绑定、清理逻辑都可以围绕一个功能集中组织这对维护大型项目非常友好。九、为什么 Hook 都要以 use 开头你肯定发现了React 官方提供的 Hook 都叫useStateuseEffectuseRefuseMemouseCallbackuseContext这是 React 的约定。凡是 Hook名字都要以use开头。包括你自己写的自定义 Hook也最好遵守这个规则function useWindowWidth() { // ... }为什么这么要求因为 React 和 ESLint 插件会根据use开头来识别这是不是一个 Hook。这样可以更好地检查代码是否符合 Hook 的使用规则。十、React Hook 的使用规则Hook 虽然很好用但它有两条非常重要的规则。1. 只能在函数组件或自定义 Hook 中调用你不能在普通的 JavaScript 函数中乱用 Hook。错误示例function test() { const [count, setCount] useState(0); }这是不允许的。正确示例function Counter() { const [count, setCount] useState(0); }或者function useCounter() { const [count, setCount] useState(0); return { count, setCount }; }2. 不要在条件语句、循环、嵌套函数中调用 Hook错误示例function Demo({ flag }) { if (flag) { const [count, setCount] useState(0); } return divDemo/div; }为什么不行因为 React 是按照 Hook 的调用顺序来管理状态的。如果你把 Hook 写在if里面那么某次渲染执行了某次渲染又没执行顺序就乱了React 就无法正确知道哪个 useState 对应哪个状态。所以一定要记住Hook 要写在组件顶层不能乱嵌套。十一、再来理解一下 Hook 的本质很多同学学 Hook 时最容易卡住的一点是函数组件不是每次渲染都会重新执行吗那它是怎么“记住状态”的这个问题问得特别好。确实函数组件每次渲染都会重新执行一遍。但是 React 内部会帮你“记住”每个 Hook 对应的数据。比如function Demo() { const [count, setCount] useState(0); const [name, setName] useState(Tom); return div{count} - {name}/div; }React 内部会根据 Hook 的调用顺序记录第一个useState对应count第二个useState对应name下次渲染时再按照同样顺序把数据取出来。这就是为什么 Hook 必须按固定顺序调用。所以从本质上说Hook 是 React 在函数组件中“挂接状态和副作用管理能力”的一种机制。十二、常见 Hook 快速总结下面把几个常见 Hook 用最简单的话概括一下。1. useState作用管理状态适合场景计数器输入框内容弹窗开关列表数据当前页码示例const [visible, setVisible] useState(false);2. useEffect作用处理副作用适合场景请求接口事件监听定时器手动操作 DOM修改页面标题示例useEffect(() { document.title 首页; }, []);3. useRef作用获取 DOM 或保存不会引起重渲染的值适合场景input 自动聚焦获取滚动容器保存定时器 id保存上一次的值示例const inputRef useRef(null);4. useMemo作用缓存计算结果适合场景复杂计算避免重复计算优化性能示例const total useMemo(() { return list.reduce((sum, item) sum item.price, 0); }, [list]);5. useCallback作用缓存函数适合场景把函数传给子组件时避免重复创建配合React.memo做性能优化示例const handleClick useCallback(() { console.log(点击了); }, []);6. useContext作用跨层级共享数据适合场景主题切换用户信息共享全局配置共享示例const theme useContext(ThemeContext);十三、初学者最容易犯的几个错误1. 把 useEffect 当成“任何逻辑都往里塞”有些初学者一学会useEffect就恨不得什么都丢进去。其实不是所有逻辑都要写进useEffect。原则是只有那些“渲染之后要做的事”才适合写进 useEffect。如果只是简单计算数据很多时候直接在组件里写就可以。2. 在 useEffect 里漏掉依赖比如useEffect(() { console.log(count); }, []);如果你的副作用里用到了count通常就应该把它写进依赖数组里useEffect(() { console.log(count); }, [count]);否则可能会出现数据不是最新值的问题。3. 把 useRef 和 useState 搞混记住一句非常关键的话需要更新页面的用 useState只是存值但不需要更新页面的用 useRef这个区别一定要分清。4. 在 if 中使用 Hook这个是典型错误。if (flag) { useEffect(() {}, []); }千万别这么写。Hook 一定要放在组件最外层。十四、一个完整小案例用 Hook 写一个待办事项列表下面我们用 Hook 写一个简单的 Todo List帮助你把useState和useEffect串起来理解。import React, { useEffect, useState } from react; function TodoApp() { const [inputValue, setInputValue] useState(); const [list, setList] useState([]); useEffect(() { const localData localStorage.getItem(todo-list); if (localData) { setList(JSON.parse(localData)); } }, []); useEffect(() { localStorage.setItem(todo-list, JSON.stringify(list)); }, [list]); const handleAdd () { if (!inputValue.trim()) return; setList([...list, inputValue]); setInputValue(); }; const handleDelete (index) { const newList list.filter((_, i) i ! index); setList(newList); }; return ( div h2Todo List/h2 input value{inputValue} onChange{(e) setInputValue(e.target.value)} placeholder请输入待办事项 / button onClick{handleAdd}添加/button ul {list.map((item, index) ( li key{index} {item} button onClick{() handleDelete(index)}删除/button /li ))} /ul /div ); } export default TodoApp;这个案例里用到了什么useState管理两个状态输入框内容inputValue待办列表list第一个useEffect页面第一次加载时从本地缓存读取数据。第二个useEffect每次list变化时把最新数据存到localStorage。这个例子特别适合初学者练手因为它同时涉及表单输入列表渲染状态更新副作用处理本地存储十五、React Hook 和类组件相比到底哪个好现在大多数新项目几乎都更倾向于函数组件 Hook原因很简单。Hook 的优点1. 代码更简洁不用写很多类组件模板代码。2. 逻辑更聚合一个功能相关的代码可以写在一起不容易分散。3. 更方便复用逻辑自定义 Hook 非常适合抽离通用能力。4. 更符合现在 React 的主流生态很多现代 React 项目、组件库、教程默认都基于 Hook。当然这并不是说类组件完全没用了。只是从开发趋势来看Hook 已经成为 React 的核心写法之一。十六、面试中怎么回答“React Hook 是什么”如果你面试时被问到这个问题可以参考下面这段回答React Hook 是 React 16.8 引入的一套新特性它允许我们在函数组件中使用状态、生命周期、副作用处理、引用、上下文等能力。Hook 的出现让函数组件不再只是无状态组件同时也让逻辑复用变得更加方便比如可以通过自定义 Hook 抽离公共逻辑。常见的 Hook 有 useState、useEffect、useRef、useMemo、useCallback、useContext 等。如果你想回答得更通俗一点也可以说Hook 就是 React 给函数组件提供的一套能力扩展机制让函数组件也能记数据、发请求、操作 DOM、复用逻辑。十七、总结学 React Hook最重要的不是一开始就把所有 Hook 全背下来而是先把最核心的三个搞懂useStateuseEffectuseRef你只要先彻底理解这三个React Hook 的大门基本就打开了。最后我们再用最简单的话总结一次useState是什么让组件记住数据。useEffect是什么让组件在渲染后执行额外操作。useRef是什么让组件获取 DOM或者保存不会触发重渲染的值。Hook 是什么Hook 就是让 React 函数组件拥有状态管理、副作用处理、DOM 操作和逻辑复用能力的一套机制。如果你之前一直觉得 Hook 很抽象那看到这里你至少应该已经明白Hook 并不是什么高深魔法它就是 React 给函数组件配的工具箱。十八、写在最后对于 React 初学者来说Hook 一开始确实会有点绕尤其是为什么要用useState为什么useEffect有依赖数组为什么 Hook 不能写在 if 里为什么函数组件每次重新执行却还能记住状态这些问题几乎每个 React 学习者都会遇到。但只要你多写几个小例子比如计数器输入框联动Todo List页面请求数据输入框自动聚焦Hook 很快就会从“抽象概念”变成“顺手工具”。建议你下一步重点练这几个方向useState控制表单和列表useEffect做接口请求和事件监听useRef获取 DOM 和保存临时值尝试自己写一个简单的自定义 Hook当你把这些练熟以后再去学useMemo、useCallback、useContext会轻松很多。