React Hooks 初学者看这一篇就够了

75 阅读5分钟

今天咱们来聊聊 React 世界里最“真香”的存在——Hooks

回想一下 React 16.8 之前被类组件 (Class Component)  支配的恐惧:到处乱飞的 this 指向、为了复用逻辑写出的“俄罗斯套娃”高阶组件(HOC)、还有那个让人头秃的生命周期图谱……

自从 Hooks 横空出世,React 开发就像换了个游戏引擎。函数组件不再是“傻瓜式”的纯展示组件,它们有了记忆、有了灵魂!

别被官方文档里那些晦涩的术语吓跑,今天咱们用说人话的方式,把 Hooks 扒个底朝天!


1. useState:组件的“便利贴”与“游戏存档”

函数组件本质上就是个普通的函数。函数执行完了,里面的变量就销毁了,像个只有7秒记忆的金鱼 。

怎么让它记住东西呢?useState 就是给组件贴的“便利贴”。

基本用法

useState 就像去银行开户,你存进去初始资金,银行给你两样东西:

  1. 余额查询 (state):现在的钱数。
  2. 存取款机 (setState):专门用来改余额的机器。

JavaScript

import React, { useState } from 'react';

const Counter = () => {
  //  游戏存档:初始化 count 为 0
  // 解构赋值:[当前状态, 修改状态的函数]
  const [count, setCount] = useState(0);

  const handleClick = () => {
    //  错误示范:直接改变量,React 根本听不见!
    // count = count + 1; 
    
    //  正确示范:用“遥控器”去通知 React 更新
    setCount(count + 1);
  };

  return (
    <div>
      <p>当前点击次数:{count} </p>
      <button onClick={handleClick}>加一</button>
    </div>
  );
};

核心知识点(敲黑板)

状态不可变性 (Immutability)
React 的 State 是只读的。你不能直接修改它(比如 user.name = 'Jack'),必须用 setState 传入一个新的值。React 只有看到新旧数据不一样了,才会去重新渲染页面。


2. useEffect:副作用的“触发器”

如果说渲染 UI 是组件的“本职工作”,那副作用 (Side Effects)  就是它的“课后作业”。

什么是副作用?

  • 去服务器拿数据 (Fetch)
  • 手动改 DOM (document.title)
  • 设置定时器 (setTimeout)

useEffect 就是帮你管理这些作业的管家。

依赖数组:useEffect 的灵魂

很多新手搞不懂 useEffect 后面那个数组 [] 是干嘛的。其实它就是一份**“监听名单”**。

  • 情况 1:不传数组(太危险了 )

    • 含义:每次渲染都执行!
    • 后果:如果你在里面更新状态,就会死循环,电脑风扇起飞。
  • 情况 2:空数组 [] (最常用 )

    • 含义:只在**组件出生(挂载)**时执行一次。
    • 比喻:就像你出生时打的疫苗,这辈子就这一次。
  • 情况 3:有依赖项 [count] (精准打击 )

    • 含义:只有当 count 变了,我才执行。
    • 比喻:这是个触发器。只有当你按下名为 count 的开关,灯泡才会亮。

JavaScript

import React, { useState, useEffect } from 'react';

const MovieList = () => {
  const [movies, setMovies] = useState([]);
  const [category, setCategory] = useState('action');

  // 场景 1:组件挂载后,立马去拿数据(相当于 componentDidMount)
  useEffect(() => {
    console.log('刚进页面,我去拉取电影列表了');
    // 伪代码:ajax('api/movies').then(data => setMovies(data));
  }, []); // 空数组 = 只执行一次

  // 场景 2:当类别变化时,重新获取数据
  useEffect(() => {
    console.log(` 类别变成了 ${category},重新加载数据...`);
  }, [category]); // 监听名单:category 变了我就动

  return (
    <div>
      <h1>电影列表</h1>
      {/* ...渲染逻辑 */}
    </div>
  );
};

3. 其他常用 Hooks:工具箱里的瑞士军刀

useContext:全校广播 / Wi-Fi 信号

  • 痛点:Props Drilling(属性钻取)。爷爷组件要把钱给孙子组件,得先给爸爸,爸爸再给儿子,儿子再给孙子...太累了!
  • 解决:useContext 就像Wi-Fi 信号。只要爷爷建了个基站(Provider),孙子拿着手机(useContext)直接连,不需要中间人传话。

useReducer:useState 的进阶版

如果你的状态逻辑很复杂(比如购物车:添加、删除、计算总价、清空...),用 useState 写一堆函数会很乱。
useReducer 适合处理复杂的“状态机” 。它接收一个“指令”(Action),然后根据指令去计算新的状态。

useMemo & useCallback:防抖大法

这两个 Hook 都是为了性能优化

  • useMemo: 缓存计算结果。像学霸的错题本,做过的难题把答案记下来,下次直接看答案,不用重新算。
  • useCallback: 缓存函数引用。防止父组件重新渲染时,生成的函数地址变了,导致子组件跟着无意义的重新渲染。

⚠ 避坑指南:不要滥用!过早优化是万恶之源。大多数情况下,你的 App 还没复杂到需要这两个东西。


4. 自定义 Hooks:封装逻辑的终极武器

这是 Hooks 最强大的地方!我们可以把组件里非 UI 的逻辑抽离出来,变成一个可复用的函数。
规则:函数名必须以 use 开头(比如 useWindowWidth, useUser)。

栗子:实时获取窗口宽度

如果不封装,每个需要宽度的组件都要写一遍 addEventListener。

JavaScript

// hooks/useWindowWidth.js
import { useState, useEffect } from 'react';

// 自定义 Hook:就是一个用了其他 Hook 的普通函数
function useWindowWidth() {
  const [width, setWidth] = useState(window.innerWidth);

  useEffect(() => {
    const handleResize = () => setWidth(window.innerWidth);
    
    // 监听窗口变化
    window.addEventListener('resize', handleResize);
    
    //  清理函数:组件销毁时移除监听(相当于 componentWillUnmount)
    return () => {
      window.removeEventListener('resize', handleResize);
    };
  }, []); // 只在挂载时监听一次

  return width; // 返回数据
}

// 在任何组件里使用,简直太优雅了!
const MyComponent = () => {
  const width = useWindowWidth(); // 一行代码搞定
  return <p>当前屏幕宽度:{width}px</p>;
};

5. Hooks 的“天条”:千万别触犯

Hooks 虽然好用,但有两个绝对不能破的规矩,否则 React 会直接报错或者行为诡异。

第一条:只能在顶层调用

千万不要在 loops (循环), conditions (if 判断), 或者 nested functions (嵌套函数) 里调用 Hooks。

JavaScript

//  错误示范
if (isAdmin) {
  const [adminData, setAdminData] = useState({}); // React 会晕!
}

//  正确示范
const [adminData, setAdminData] = useState({});
if (isAdmin) {
  // 逻辑写在这里
}

原因:React 是根据 Hooks 调用的顺序来记录状态的。如果你在 if 里写 Hook,下次渲染 if 没进去,顺序就乱了,张三的帽子戴到了李四头上。

第二条:依赖项要写全

在 useEffect 里用到了哪个外部变量,就一定要把它加到依赖数组 [] 里。
后果:如果你欺骗 React,就会产生闭包陷阱 (Stale Closure) ,你会发现你的 count 永远停留在旧值,怎么加都不变。


结语

Hooks 的出现,让 React 变得更加函数式、更加灵活。

  • useState 给了我们记忆。
  • useEffect 帮我们处理脏活累活。
  • 自定义 Hooks 让我们成为了逻辑复用的大师。

看完这篇,是不是觉得 Hooks 也没那么可怕?赶紧打开你的编辑器,把那些老旧的 Class 组件重构一遍吧!