八个 React Hooks 快速上手:从入门到实战的个人总结

0 阅读6分钟

好久没写技术文章了,最近翻看了自己刚学 React 时做的笔记,决定整理一下我常用的八个 React Hooks(如果算上自定义 Hooks 应该是九个,但今天先聚焦这八个)。我刚开发时是个 React 小白,看了一天公司项目后发现必须补习 Hooks,于是周末恶补了一波,做了些总结。这篇文章将结合代码示例和实际场景,带你逐一掌握这些 Hooks。虽然内容有点长,但你可以挑自己感兴趣的 Hooks 重点阅读。准备好了吗?让我们一起解锁 React Hooks 的魅力吧!

1. useState:状态管理的起点

类组件 vs 函数组件

在 React 中,状态管理是核心。类组件用 this.state 定义状态,而函数组件则靠 useState。来看看两者的对比:

类组件实现

import React from 'react';

class StateClass extends React.Component {
  constructor() {
    super();
    this.state = { name: '类' };
  }

  setName = () => {
    this.setState({ name: '我通过类组件方法变成这样了' });
  };

  render() {
    return (
      <div onClick={this.setName}>
        这是一个类组件 —— {this.state.name}
      </div>
    );
  }
}

export default StateClass;

函数组件实现

import React, { useState } from 'react';

function StateFunction() {
  const [name, setName] = useState('函数');

  return (
    <div onClick={() => setName('我使用 Hooks 变成这样了')}>
      这是一个函数组件 —— {name}
    </div>
  );
}

export default StateFunction;

使用要点

  • useState 返回一个数组:[状态值, 更新函数]。
  • 初始值可以是任意类型(字符串、对象等)。
  • 更新函数支持直接赋值或基于前值的回调(如 setName(val => val + 'xxx'))。

场景:需要管理简单状态时,比如表单输入、计数器等。相比类组件,useState 写法更简洁,直观易懂。


2. useEffect:副作用的魔法师

什么是副作用?

副作用是指渲染之外的操作,比如数据请求、事件绑定、手动改 DOM 等。useEffect 就是为函数组件添加“生命周期”功能的利器,执行时机在渲染结束后。

无依赖:每次渲染都执行

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

function Counter() {
  const [num, setNum] = useState(0);

  useEffect(() => {
    console.log('渲染结束啦!');
  });

  return (
    <div onClick={() => setNum(num + 1)}>
      点击我 —— {num}
    </div>
  );
}

空依赖:只执行一次

useEffect(() => {
  console.log('只在首次渲染时执行');
}, []);

指定依赖:依赖变化时执行

useEffect(() => {
  console.log('num 变了,重新执行');
}, [num]);

清除副作用:避免内存泄漏

绑定事件时,必须清理,否则会重复绑定导致性能问题:

useEffect(() => {
  const updateMouse = (e) => {
    console.log(`鼠标位置:${e.clientX}, ${e.clientY}`);
  };
  document.addEventListener('click', updateMouse);

  return () => {
    document.removeEventListener('click', updateMouse);
    console.log('清理完成');
  };
}, []);

执行顺序

  1. 首次渲染:执行 useEffect 的回调。
  2. 更新时:先执行上次的清理函数(return),再执行新的回调。
  3. 卸载时:执行最后一次清理。

场景:数据请求、事件监听、定时器等。空依赖适合初始化加载,带依赖适合动态更新。


3. useLayoutEffect:DOM 操作的精准助手

与 useEffect 的区别

useLayoutEffect 和 useEffect 用法相同,但执行时机不同:

示例对比

  • useEffect:渲染结束后执行(异步)。

  • useLayoutEffect:DOM 更新后、渲染前执行(同步)。

  • import React, { useState, useEffect, useLayoutEffect } from 'react';
    
    function Demo() {
      const [num, setNum] = useState(0);
    
      useLayoutEffect(() => {
        console.log('useLayoutEffect 先执行');
      }, [num]);
    
      useEffect(() => {
        console.log('useEffect 后执行');
      }, [num]);
    
      return (
        <div onClick={() => setNum(num + 1)}>
          点击我 —— {num}
        </div>
      );
    }
    

    场景:需要同步操作 DOM(比如测量元素尺寸、调整布局)时用 useLayoutEffect,其他情况优先 useEffect 以避免阻塞渲染。


    4. useMemo:性能优化的利器

    作用

    useMemo 缓存计算结果,只有依赖变化时才重新计算,避免不必要的重复执行。

    优化复杂计算

  • import React, { useState, useMemo } from 'react';
    
    function Calculator() {
      const [num, setNum] = useState(1);
      const [age, setAge] = useState(18);
    
      const doubleNum = useMemo(() => {
        console.log(`计算双倍:${num}`);
        return num * 2; // 假设复杂计算
      }, [num]);
    
      return (
        <div onClick={() => setAge(age + 1)}>
          双倍值:{doubleNum} <br />
          年龄:{age}
        </div>
      );
    }
    

    未优化:每次渲染都重新计算 doubleNum,即使 num 不变。

  • 优化后:只有 num 变化时才重新计算。

    优化子组件渲染

  • import React, { useState, useMemo } from 'react';
    import { memo } from 'react';
    
    const Child = memo(({ info }) => {
      console.log('子组件渲染');
      return <p>姓名:{info.name}</p>;
    });
    
    function Parent() {
      const [show, setShow] = useState(true);
    
      const info = useMemo(() => ({ name: 'Even', age: 22 }), []);
    
      return (
        <div>
          <Child info={info} />
          <button onClick={() => setShow(!show)}>切换</button>
        </div>
      );
    }
    

    场景:复杂计算、防止子组件因父组件无关更新而重复渲染。


    5. useCallback:函数缓存的专家

    与 useMemo 的区别

  • useMemo:缓存返回值(可以是任意类型)。

  • useCallback:缓存函数本身。

  • import React, { useState, useMemo, useCallback } from 'react';
    
    function Demo() {
      const [num, setNum] = useState(1);
      const [age, setAge] = useState(18);
    
      const memoizedValue = useMemo(() => num * 2, [num]); // 返回值
      const memoizedFn = useCallback(() => num * 2, [num]); // 返回函数
    
      return (
        <div onClick={() => setAge(age + 1)}>
          Memo 值:{memoizedValue} <br />
          Callback 值:{memoizedFn()} <br />
          年龄:{age}
        </div>
      );
    }
    

    优化子组件

  • import React, { useState, useCallback } from 'react';
    
    function Child({ callback }) {
      useEffect(() => {
        console.log('子组件更新');
      }, [callback]);
      return <p>结果:{callback}</p>;
    }
    
    function Parent() {
      const [num, setNum] = useState(1);
    
      const getDoubleNum = useCallback(() => num * 2, [num]);
    
      return (
        <div onClick={() => setNum(num + 1)}>
          <Child callback={getDoubleNum()} />
          父组件:{num}
        </div>
      );
    }
    

    场景:传递函数给子组件时,避免因函数引用变化导致子组件重复渲染。


    6. useRef:持久化数据的守护者

    作用

    useRef 返回一个在组件生命周期内保持不变的引用对象,常用于保存数据或 DOM 引用。

    示例:清除定时器

  • import React, { useState, useEffect, useRef } from 'react';
    
    function Timer() {
      const [num, setNum] = useState(0);
      const timerRef = useRef();
    
      useEffect(() => {
        timerRef.current = setInterval(() => setNum(n => n + 1), 400);
      }, []);
    
      useEffect(() => {
        if (num > 10) {
          clearInterval(timerRef.current);
          console.log('定时器已清除');
        }
      }, [num]);
    
      return <div>计数:{num}</div>;
    }
    

    未使用 useRef:定时器 ID 丢失,无法清除。 使用 useRef:通过 ref.current 保存 ID,精准清除。

    特点:修改 ref.current 不会触发重新渲染。

    场景:保存定时器、访问 DOM 元素(如聚焦输入框)。


    7. useContext:状态共享的桥梁

    作用

    useContext 让子组件轻松共享父组件的状态,避免逐层传递 props。

    未优化

  • function Parent() {
      const [num, setNum] = useState(1);
      return (
        <div>
          <button onClick={() => setNum(num + 1)}>加 1</button>
          <Child1 num={num} />
          <Child2 num={num} />
        </div>
      );
    }
    

    使用 useContext

  • import React, { useState, useContext, createContext } from 'react';
    
    const NumContext = createContext(null);
    
    function Parent() {
      const [num, setNum] = useState(1);
    
      return (
        <NumContext.Provider value={num}>
          <button onClick={() => setNum(num + 1)}>加 1</button>
          <Child1 />
          <Child2 />
        </NumContext.Provider>
      );
    }
    
    function Child1() {
      const num = useContext(NumContext);
      return <p>子组件 1:{num}</p>;
    }
    
    function Child2() {
      const num = useContext(NumContext);
      return <p>子组件 2:{num + 2}</p>;
    }
    

    8. useReducer:复杂状态的掌舵手

    作用

    useReducer 类似 Redux,适合管理复杂状态逻辑。

    示例

  • import React, { useReducer } from 'react';
    
    const initialState = { age: 18, num: 1 };
    
    const reducer = (state, action) => {
      switch (action.type) {
        case 'add':
          return { ...state, num: state.num + 1 };
        default:
          return state;
      }
    };
    
    function Counter() {
      const [state, dispatch] = useReducer(reducer, initialState);
    
      return (
        <div>
          <button onClick={() => dispatch({ type: 'add' })}>加 1</button>
          计数:{state.num} <br />
          年龄:{state.age}
        </div>
      );
    }
    

  • 总结:Hooks 让 React 更简单

    这八个 Hooks 是 React 函数组件的基石:

  • useState:基础状态管理。

  • useEffect / useLayoutEffect:处理副作用。

  • useMemo / useCallback:性能优化。

  • useRef:持久化数据。

  • useContext:状态共享。

  • useReducer:复杂状态管理。

  •  

    从类组件的繁琐到 Hooks 的简洁,React 的开发体验变得更优雅。希望这篇文章能帮你快速上手这些 Hooks,并在项目中灵活运用。如果你有疑问或想聊聊自定义 Hooks,欢迎留言交流!

    关键词:React Hooks 教程、useState 示例、useEffect 副作用、useMemo 优化、React 函数组件开发。