React中常用的Hook(useEffect、useRef、useMemo、useNavigate、useParams)

React hook1:useEffect

在编程中,副作用是指函数或表达式在执行过程中对外部环境产生影响的行为。例如:

  • 修改外部变量(如全局变量、DOM、API 请求、设置定时器等)

什么是纯函数?

javascript 复制代码
// 纯函数:输入相同,输出必定相同,不影响外部
function add(a, b) {
  return a + b; 
}
//有副作用的函数:影响外部状态
function updateDOM() {
  document.title = "Changed!"; //副作用:修改DOM
}

React 推崇函数式编程,核心思想是:

  • 组件应该是纯函数(给定相同的props和state,渲染相同的 UI)
  • 副作用应该被隔离,不能直接在渲染过程中执行

React 如何处理副作用?渲染和外部处理相分离。

由于渲染必须是纯的,React 提供了useEffect 让副作用在渲染完成后执行,而不是在渲染期间执行。

useEffect是 React 提供的副作用隔离机制,让不纯的操作(如API请求、DOM修改)不影响组件的纯渲染逻辑,同时提供清理和优化能力。

  1. 保持渲染纯净(在组件渲染到屏幕之后运行)
  2. 避免阻塞渲染
  3. 自动清理副作用(可以返回一个清理函数,在组件卸载时执行)
javascript 复制代码
react jsx title="useEffect基础用法"
import { useEffect } from 'react';

function Example() {
  useEffect(() => {
    console.log('组件首次渲染后执行');
  }, []); // 空依赖数组表示只运行一次

  return <div>组件内容</div>;
}
javascript 复制代码
react jsx title="useEffect引入依赖项监听变化"
function Example({ id }) {
  useEffect(() => {
    console.log('id 变化时执行:', id);
  }, [id]); // 仅在 id 变化时重新执行

  return <div>ID: {id}</div>;
}
javascript 复制代码
react jsx title="useEffect清理副作用"
function Example() {
  useEffect(() => {
    const timer = setInterval(() => {
      console.log('定时器运行中...');
    }, 1000);

    return () => {
      clearInterval(timer); // 组件卸载时清理定时器
    };
  }, []);

  return <div>查看控制台</div>;
}

React hook2:useRef

useRef用于直接操作 DOM或存储可变值(不会触发重新渲染)。

用法1:操作DOM

javascript 复制代码
import { useRef } from 'react';

function InputFocus() {
  const inputRef = useRef(null); // 1. 创建 ref

  const handleClick = () => {
    inputRef.current.focus(); // 3. 通过 .current 操作 DOM
  };

  return (
    <div>
      {/* 2. 绑定 ref 到元素 */}
      <input ref={inputRef} type="text" />
      <button onClick={handleClick}>聚焦输入框</button>
    </div>
  );
}

用法2:存储可变值,不触发重新渲染

typescript 复制代码
import { useRef, useState } from 'react';

function Counter() {
  const [count, setCount] = useState(0);
  const renderCount = useRef(0); // 存储组件渲染次数(不会触发更新)

  renderCount.current += 1; // 修改值(不会导致重新渲染)

  return (
    <div>
      <p>Count: {count}</p>
      <p>组件渲染次数: {renderCount.current}</p>
      <button onClick={() => setCount(count + 1)}>增加</button>
    </div>
  );
}

useRef的底层机制

javascript 复制代码
// 近似实现(React 内部简化逻辑)
function useRef(initialValue) {
  return { current: initialValue }; // 每次都返回同一个对象!
}
  • 存储原理:React 在组件首次渲染时创建这个{ current: ... }对象,后续所有渲染都返回同一个对象(引用不变)。
  • 修改current:直接修改current属性不会触发渲染,因为对象本身的引用没变,React 不会检测到变化。

为什么操作 DOM 需要绑定到标签?

html 复制代码
<input ref={inputRef} /> 

React 的隐藏逻辑:当ref属性绑定到 DOM 标签时,React 会在渲染结束后自动将 DOM 节点赋值给inputRef.current。

javascript 复制代码
// React 内部类似这样处理
const domNode = document.createElement('input');
inputRef.current = domNode; // 自动填充 DOM 节点

存储变量为何不需要绑定?

javascript 复制代码
const renderCount = useRef(0);
renderCount.current += 1; // 直接修改即可

存储变量时,current只是一个普通属性,你可以自由读写,React 不会干涉它的值。

关键区别总结

场景 是否需要绑定到标签 current的值来源
操作 DOM 需要 由 React 自动填充为 DOM 节点
存储可变值 不需要 由开发者手动赋值/修改

React hook3:useMemo

useMemo用于缓存计算结果,只有当依赖项变化时才重新计算,避免重复渲染时不必要的计算开销。

javascript 复制代码
function UserList({ users, searchTerm }) {
  // 缓存过滤后的用户列表,只有当users或searchTerm变化时才重新过滤
  const filteredUsers = useMemo(() => {
    return users.filter(user => 
      user.name.toLowerCase().includes(searchTerm.toLowerCase())
    );
  }, [users, searchTerm]);

  return (
    <ul>
      {filteredUsers.map(user => (
        <li key={user.id}>{user.name}</li>
      ))}
    </ul>
  );
}

React hook4:useNavigate

**useNavigate 就像一个提供多种导航工具的"工具箱",可以从中选择需要的功能来完成路由跳转、前进/后退、传递数据等操作。**用"工具箱"类比 useNavigate:

工具箱里的工具 对应 useNavigate 的功能
导航地图 navigate('/path') ------ 直接跳转到指定路径。
橡皮擦 navigate('/path', { replace: true }) ------ 替换当前路由(擦掉当前记录,不保留历史)。
时间机器按钮 navigate(1) 或 navigate(-1) ------ 前进或后退到历史记录中的页面。
快递包裹 navigate('/path', { state: { data } }) ------ 跳转时携带隐藏数据(偷偷塞个包裹给下个页面)。

React hook5:useParams()

useParams从当前 URL 路径中提取参数。

示例代码:

javascript 复制代码
<Route path="/user/:id" element={<UserProfile />} />
javascript 复制代码
import { useParams } from 'react-router-dom';

function UserProfile() {
  // 提取 URL 中的动态参数(比如 :id)
  const params = useParams();
  console.log(params.id); // 访问 /user/123 时,输出 "123"

  return <h1>用户ID: {params.id}</h1>;
}

当用户访问 /user/123 时,useParams() 会提取出 { id: '123' }。