3、导航与链接:Link与NavLink组件、activeClassName与activeStyle、编程式导航(useNavigate)

导航,说白了就是让用户在页面之间跳来跳去。React Router 提供了几种方式,我一个个讲给你听。

3.1 Link 组件:最基础的导航

Link 组件是 React Router 里最常用的导航方式。它最终会渲染成一个 <a> 标签,但不会触发页面刷新。

import { Link } from 'react-router-dom';

function Navbar() {
  return (
    <nav>
      <Link to="/">首页</Link>
      <Link to="/about">关于我们</Link>
      <Link to="/products">产品列表</Link>
    </nav>
  );
}

to 属性支持字符串和对象两种写法。我个人习惯用字符串,简单直接。但有些场景需要传参数,就得用对象了。

// 字符串写法
<Link to="/users/123">用户详情</Link>

// 对象写法
<Link to={{
  pathname: '/users/123',
  search: '?tab=profile',
  hash: '#bio'
}}>用户详情</Link>
小提示:Link 组件默认会阻止浏览器的默认跳转行为,所以不用担心页面刷新。它内部用的是 history.pushhistory.replace

3.2 NavLink 组件:带激活状态的导航

NavLink 是 Link 的升级版。它多了一个功能:能自动识别当前路由是否匹配,然后给你加上激活样式。

我在项目中遇到过这样的需求:导航栏里当前选中的菜单要高亮显示。用 NavLink 就特别方便。

import { NavLink } from 'react-router-dom';

function Navbar() {
  return (
    <nav>
      <NavLink to="/" end>首页</NavLink>
      <NavLink to="/about">关于我们</NavLink>
      <NavLink to="/products">产品列表</NavLink>
    </nav>
  );
}

注意那个 end 属性。为什么要加它?因为如果不加,当你在 /about 页面时,/ 也会被匹配到。嗯,这里要注意,end 属性表示「精确匹配」,只有路径完全一致时才激活。

activeClassName 和 activeStyle

这两个属性就是用来控制激活样式的。一个是加类名,一个是直接写内联样式。

// 使用 activeClassName
<NavLink 
  to="/about" 
  className={({ isActive }) => isActive ? 'active-link' : ''}
>
  关于我们
</NavLink>

// 使用 activeStyle(旧版写法,现在推荐用 className 函数)
<NavLink 
  to="/about" 
  style={({ isActive }) => ({
    color: isActive ? '#ff6b6b' : '#333',
    fontWeight: isActive ? 'bold' : 'normal'
  })}
>
  关于我们
</NavLink>
重要更新:React Router v6 已经废弃了 activeClassNameactiveStyle 属性。现在推荐用 classNamestyle 的回调函数形式。你想想看,这样更灵活,还能根据状态做更多判断。

我曾经在维护一个老项目时,发现代码里还在用 activeClassName,结果升级到 v6 后全报错了。所以建议你直接学新写法。

3.3 编程式导航:useNavigate

有时候你不能用 Link 组件,比如表单提交成功后要跳转,或者倒计时结束后自动跳转。这时候就需要编程式导航。

React Router v6 提供了 useNavigate 这个 Hook,用起来特别顺手。

import { useNavigate } from 'react-router-dom';

function LoginForm() {
  const navigate = useNavigate();

  const handleSubmit = async (event) => {
    event.preventDefault();
    
    // 模拟登录请求
    await login();
    
    // 登录成功后跳转到首页
    navigate('/');
  };

  return (
    <form onSubmit={handleSubmit}>
      <input type="text" placeholder="用户名" />
      <input type="password" placeholder="密码" />
      <button type="submit">登录</button>
    </form>
  );
}

navigate 函数支持两个参数:

参数 类型 说明
第一个参数 string 或 number 要跳转的路径,或者步数(如 -1 表示后退)
第二个参数 object(可选) 配置项,如 replace、state 等
// 前进或后退
navigate(-1);  // 后退一页
navigate(1);   // 前进一页

// 替换当前历史记录
navigate('/dashboard', { replace: true });

// 携带状态数据
navigate('/profile', { 
  state: { userId: 123, from: 'login' } 
});
避坑指南:我曾经在 useEffect 里直接调用 navigate,结果控制台报了一堆警告。为什么?因为 navigate 会触发组件重新渲染,容易造成死循环。正确的做法是:在事件处理函数或回调里调用,或者用 useEffect 的依赖数组控制好时机。

3.4 三种导航方式的对比

方式 适用场景 特点
Link 静态导航链接 简单、声明式、无激活状态
NavLink 需要高亮当前菜单 自动匹配路由、支持激活样式
useNavigate 表单提交、定时跳转、条件跳转 灵活、可在任意函数中调用

说白了,能用 Link 和 NavLink 的地方,尽量别用编程式导航。声明式写法更清晰,也更容易维护。但遇到需要「在代码里决定跳不跳」的场景,useNavigate 就是你的好帮手。

我的习惯:导航栏用 NavLink,普通链接用 Link,业务逻辑里的跳转用 useNavigate。这样分工明确,代码也容易读。

嗯,导航这块内容就这些。你想想看,其实核心就三个点:Link 做基础导航,NavLink 做高亮导航,useNavigate 做灵活跳转。掌握了这些,大部分导航需求都能搞定。