Next.js App Router 中的 Hook

59 阅读4分钟

useSearchParams 

主要功能是让你在客户端组件中,读取和操作当前 URL 的查询参数,也就是 URL 中 ? 后面的部分。

🎯 核心功能:读取查询参数

想象一下,你的 URL 是这样的 https://example.com/search?keyword=nextjs&page=2&sort=popular

useSearchParams 就是一个专门用来获取 keyword=nextjs&page=2&sort=popular 这部分数据的工具。

1. 基本用法

useSearchParams 只能在 客户端组件 中使用。使用 "use client"; 指令来声明一个组件为客户端组件。

2. 常用 URLSearchParams API 方法

useSearchParams 返回的对象提供了以下常用方法:

方法描述示例
get(name)获取第一个匹配的参数值。如果不存在,返回 nullsearchParams.get('page')
getAll(name)获取所有匹配的参数值(返回数组)。searchParams.getAll('tag')
has(name)检查某个参数是否存在(返回 true/false)。searchParams.has('sort')
toString()将所有参数序列化回字符串。searchParams.toString()
forEach()遍历所有参数。searchParams.forEach((value, key) => ...)

useRouter 

主要功能是让你在客户端组件中,通过代码来控制页面的跳转、刷新、返回等操作,而不是依赖用户点击 <a> 标签。

1. 基本用法

和 useSearchParams 一样,useRouter 也只能在客户端组件中使用。

📚 router 对象的常用方法

useRouter() 返回的 router 对象提供了以下核心方法:

方法描述示例
push(path)最常用。将新 URL 添加到浏览器历史记录栈,并导航到该页面。router.push('/about')
replace(path)导航到新 URL,但不会在历史记录栈中添加新条目,而是替换当前条目。router.replace('/login')
back()导航到历史记录中的上一个页面。router.back()
forward()导航到历史记录中的下一个页面。router.forward()
refresh()重新获取当前页面的数据并重新渲染,但不会改变 URL。类似于刷新页面,但更快。router.refresh()
prefetch(path)预加载指定路径的页面。当用户可能要访问该页面时,可以提前加载,提升用户体验。router.prefetch('/dashboard')

2. push vs replace 的区别

这是一个关键区别,直接影响用户体验。

  • router.push('/login') :

    • 用户在 /home 页面点击登录。
    • URL 变为 /login
    • 浏览器历史记录栈:/home -> /login
    • 用户点击浏览器的“返回”按钮,会回到 /home 页面
  • router.replace('/login') :

    • 用户在 /home 页面点击登录。
    • URL 变为 /login
    • 浏览器历史记录栈:/login ( /home 被替换了)。
    • 用户点击浏览器的“返回”按钮,会回到 /home 之前的页面,而不是 /home

使用场景

  • push:用于正常的页面跳转,用户可能需要返回。
  • replace:用于重定向场景。例如,未登录用户访问受保护的页面 /dashboard,你用 replace 将其跳转到 /login,这样用户登录后就不应该再“返回”到需要登录的 /dashboard 页面了。

3. router.refresh() 的强大之处

router.refresh() 是 App Router 中一个非常有用的方法。它不会丢失客户端状态(如表单输入、滚动位置),但会向服务器发起一次请求,重新获取数据并重新渲染服务组件。

🚀 实战场景:结合 useRouter 更新查询参数

这是一个非常常见的组合:读取当前参数,然后根据用户操作更新 URL。

场景:一个分页组件,点击页码时更新 page 参数。

代码解析

// app/components/Pagination.jsx
"use client";

import { useRouter, useSearchParams, usePathname } from 'next/navigation';

export default function Pagination({ totalPages }) {
  const router = useRouter();
  const pathname = usePathname(); // 获取当前路径,如 '/search'
  const searchParams = useSearchParams();
  const currentPage = Number(searchParams.get('page')) || 1;

  const createPageURL = (pageNumber) => {
    const params = new URLSearchParams(searchParams); // 复制当前所有参数
    params.set('page', pageNumber.toString()); // 更新 page 参数
    return `${pathname}?${params.toString()}`; // 构建新 URL
  };

  return (
    <div>
      {/* 上一页 */}
      <button
        onClick={() => router.push(createPageURL(currentPage - 1))}
        disabled={currentPage <= 1}
      >
        上一页
      </button>

      <span> 第 {currentPage} 页 </span>

      {/* 下一页 */}
      <button
        onClick={() => router.push(createPageURL(currentPage + 1))}
        disabled={currentPage >= totalPages}
      >
        下一页
      </button>
    </div>
  );
}

  1. usePathname() 获取当前路径部分(不含查询参数)。
  2. new URLSearchParams(searchParams) 创建一个参数对象的副本,这样我们修改它时不会影响原始对象。
  3. params.set('page', ...) 更新页码。
  4. router.push() 导航到新构建的 URL,页面会自动刷新并显示新数据。

📌 总结

Hook主要功能典型场景
useRouter改变 URL,执行导航操作。按钮点击跳转、登录后重定向、刷新数据。
useSearchParams读取 URL 的查询参数。获取搜索关键词、筛选条件、页码。
usePathname读取 URL 的路径部分。构建新 URL 时保留当前路径。