React 第五十四节 Router中useRevalidator的使用详解及案例分析

news2026/3/8 3:31:12

前言

useRevalidatorReact Router v6.4+ 引入的一个强大钩子,用于在数据路由(Data Router)中手动触发路由数据的重新验证(revalidation)。
它在需要主动刷新数据而不改变路由位置的场景中非常有用。

一、useRevalidator 核心用途

手动数据刷新:用户触发数据重新加载(如点击刷新按钮)

轮询机制:定期更新数据(如实时仪表盘)

乐观更新后同步:在本地状态变更后与服务器同步

外部事件响应:响应 WebSocket 消息或其他外部事件

二、useRevalidator 钩子返回对象

useRevalidator 返回一个包含两个属性的对象:

  1. revalidate:触发重新验证的函数
  2. state:当前重新验证状态(idle 或 loading

三、useRevalidator 基本用法示例

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

function RefreshButton() {
  const { revalidate, state } = useRevalidator();
  
  return (
    <button 
      onClick={() => revalidate()}
      disabled={state === 'loading'}
    >
      {state === 'loading' ? '刷新中...' : '刷新数据'}
    </button>
  );
}

四、useRevalidator 实际应用场景

4.1、用户手动刷新数据

import { useRevalidator, useLoaderData } from 'react-router-dom';

function UserDashboard() {
  const { users } = useLoaderData();
  const { revalidate, state } = useRevalidator();
  const [lastUpdated, setLastUpdated] = useState(new Date());

  const handleRefresh = () => {
    revalidate();
    setLastUpdated(new Date());
  };

  return (
    <div className="dashboard">
      <div className="dashboard-header">
        <h1>用户管理</h1>
        <div className="controls">
          <button 
            onClick={handleRefresh}
            disabled={state === 'loading'}
            className="refresh-btn"
          >
            <span className="icon">🔄</span>
            {state === 'loading' ? '加载中...' : '刷新数据'}
          </button>
          <p className="update-time">
            最后更新: {lastUpdated.toLocaleTimeString()}
          </p>
        </div>
      </div>
      
      <UserList users={users} loading={state === 'loading'} />
    </div>
  );
}

function UserList({ users, loading }) {
  if (loading) {
    return <div className="loading-indicator">加载用户数据...</div>;
  }
  
  return (
    <ul className="user-list">
      {users.map(user => (
        <li key={user.id} className="user-card">
          <div className="avatar">{user.name.charAt(0)}</div>
          <div className="user-info">
            <h3>{user.name}</h3>
            <p>{user.email}</p>
          </div>
        </li>
      ))}
    </ul>
  );
}

4.2、实时数据轮询

import { useEffect } from 'react';
import { useRevalidator } from 'react-router-dom';

function RealTimeStockTicker() {
  const { revalidate } = useRevalidator();
  
  // 每15秒自动刷新数据
  useEffect(() => {
    const intervalId = setInterval(() => {
      revalidate();
    }, 15000);
    
    return () => clearInterval(intervalId);
  }, [revalidate]);
  
  return (
    <div className="ticker">
      {/* 股票数据展示 */}
    </div>
  );
}

// 完整示例:股票监控仪表盘
function StockDashboard() {
  const { stocks } = useLoaderData();
  const { revalidate, state } = useRevalidator();
  
  return (
    <div className="stock-dashboard">
      <div className="dashboard-controls">
        <h2>实时股票行情</h2>
        <div className="auto-refresh">
          <label>
            <input 
              type="checkbox" 
              onChange={e => setAutoRefresh(e.target.checked)} 
            />
            自动刷新 (15)
          </label>
        </div>
        <button 
          onClick={() => revalidate()}
          className="refresh-btn"
        >
          手动刷新
        </button>
      </div>
      
      {state === 'loading' ? (
        <div className="loading-overlay">
          <div className="spinner"></div>
          <p>更新行情数据...</p>
        </div>
      ) : null}
      
      <StockTable stocks={stocks} />
    </div>
  );
}

4.3、乐观更新后重新验证

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

function TodoItem({ todo }) {
  const { revalidate } = useRevalidator();
  const [isUpdating, setIsUpdating] = useState(false);
  
  const toggleCompleted = async () => {
    // 乐观更新:立即更新UI
    setIsUpdating(true);
    
    try {
      // 发送API请求
      await updateTodoStatus(todo.id, !todo.completed);
      
      // 成功:重新验证数据
      revalidate();
    } catch (error) {
      // 错误处理
      console.error('更新失败:', error);
    } finally {
      setIsUpdating(false);
    }
  };
  
  return (
    <li className={`todo-item ${todo.completed ? 'completed' : ''}`}>
      <input
        type="checkbox"
        checked={todo.completed}
        onChange={toggleCompleted}
        disabled={isUpdating}
      />
      <span className="todo-text">{todo.text}</span>
      {isUpdating && <span className="updating-indicator">更新中...</span>}
    </li>
  );
}

4.4、WebSocket 实时更新

import { useEffect } from 'react';
import { useRevalidator } from 'react-router-dom';

function ChatRoom() {
  const { messages } = useLoaderData();
  const { revalidate } = useRevalidator();
  
  useEffect(() => {
    const socket = new WebSocket('wss://example.com/chat');
    
    socket.addEventListener('message', (event) => {
      const data = JSON.parse(event.data);
      
      if (data.type === 'NEW_MESSAGE') {
        // 收到新消息时重新验证数据
        revalidate();
      }
    });
    
    return () => socket.close();
  }, [revalidate]);
  
  return (
    <div className="chat-room">
      <MessageList messages={messages} />
      <MessageInput />
    </div>
  );
}

五、useRevalidator 高级用法:带错误处理的重试机制

import { useState } from 'react';
import { useRevalidator } from 'react-router-dom';

function DataPanel() {
  const { data, error } = useLoaderData();
  const { revalidate, state } = useRevalidator();
  const [retryCount, setRetryCount] = useState(0);
  
  const handleRetry = () => {
    revalidate();
    setRetryCount(prev => prev + 1);
  };
  
  if (error) {
    return (
      <div className="error-panel">
        <div className="error-message">
          <h3>数据加载失败</h3>
          <p>{error.message}</p>
          <p>尝试次数: {retryCount}</p>
        </div>
        
        <div className="error-actions">
          <button 
            onClick={handleRetry}
            disabled={state === 'loading'}
          >
            {state === 'loading' ? '重试中...' : '重试加载'}
          </button>
          
          {retryCount > 2 && (
            <button onClick={() => window.location.reload()}>
              刷新页面
            </button>
          )}
        </div>
      </div>
    );
  }
  
  return <DataDisplay data={data} />;
}

六、useRevalidator 与相关API对比

方法 用途 特点
useRevalidator: 用于手动重新验证路由数据; 具有不改变路由位置,仅刷新数据的特点
useNavigate: 用于编程式导航; 具有改变路由位置,触发新数据加载的特点
useFetcher: 用于提交数据或加载数据; 具有不触发导航,适合局部操作的特点
loader函数: 用于路由数据加载; 具有自动执行的路由数据获取的特点

七、最佳实践

1、用户反馈:在重新验证期间提供加载状态

2、防抖处理:避免频繁触发重新验证

3、错误处理:妥善处理重新验证失败情况

4、资源清理:清除轮询和事件监听器

5、性能优化:避免不必要的重新验证

// 带防抖的刷新按钮
function DebouncedRefreshButton() {
  const { revalidate, state } = useRevalidator();
  const debouncedRevalidate = useDebounce(revalidate, 300);
  
  return (
    <button 
      onClick={debouncedRevalidate}
      disabled={state === 'loading'}
    >
      安全刷新
    </button>
  );
}

// 自定义防抖钩子
function useDebounce(callback, delay) {
  const timerRef = useRef();
  
  return (...args) => {
    clearTimeout(timerRef.current);
    timerRef.current = setTimeout(() => {
      callback(...args);
    }, delay);
  };
}

八、注意事项

  1. 数据路由要求:必须在数据路由中使用(使用 createBrowserRouter 等)
  2. 状态管理state 仅表示重新验证状态,不表示初始加载
  3. 组件卸载:确保在组件卸载时清理轮询事件监听
  4. 数据变化:重新验证会触发所有活动路由的 loader 重新执行
  5. 错误边界:重新验证中的错误会被路由错误边界捕获

总结

useRevalidator 是 React Router 数据路由模型中的关键工具,它解决了在不改变路由位置的情况下刷新数据的常见需求。通过合理使用此钩子,我们可以:

a、创建用户友好的数据刷新机制

b、实现实时数据更新功能

c、构建健壮的乐观更新流程

d、响应外部数据变化事件

e、提升应用的数据同步能力

此钩子特别适合需要保持UI与服务器数据同步的复杂应用场景,是构建现代数据驱动应用的强大工具。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2402067.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【C语言预处理详解(下)】--#和##运算符,命名约定,命令行定义 ,#undef,条件编译,头文件的包含,嵌套文件包含,其他预处理指令

目录 五.#和##运算符 5.1--#运算符 5.2--##运算符 六.命名约定&#xff0c;#undef&#xff0c;命令行定义 6.1--命名约定 6.2--#undef 6.3--命名行定义 七.条件编译 常见的条件编译指令&#xff1a; 1.普通的条件编译&#xff1a; 2.多个分支的条件编译(可以利用条…

03.搭建K8S集群

K8S集群搭建的方式 目前主流的搭建k8s集群的方式有kubeadm、minikube、二进制包三种方式&#xff1a; kubeadm&#xff08;本案例搭建方式&#xff09; 是一个工具&#xff0c;用于快速搭建kubernetes集群&#xff0c;目前应该是比较方便和推荐的&#xff0c;简单易用 kubea…

RDMA简介3之四种子协议对比

RDMA协议共有四种子协议&#xff0c;分别为InfiniBand、iWARP、RoCE v1和RoCE v2协议。这四种协议使用统一的RDMA API&#xff0c;但在具体的网络层级实现上有所不同&#xff0c;如图1所示&#xff0c;接下来将分别介绍这四种子协议。 图1 RDMA四种子协议网络层级关系图 Infin…

【最新版】西陆洗车系统源码全开源+uniapp前端+搭建教程

一.系统介绍 一款基于ThinkPHPUniapp开发的多门店洗车系统&#xff0c;包含用户端&#xff08;小程序&#xff09;、门店员工端&#xff08;小程序&#xff09;、门店端&#xff08;PC&#xff09;、平台管理端&#xff08;PC&#xff09;。 门店分连锁门店和独立门店&#xf…

Linux开发工具(apt,vim,gcc)

目录 yum/apt包管理器 Linux编辑器 vim 1.见一见vim 2.vim的多模式 3.命令模式底行模式等 4.vim的配置 Linux编译器 gcc/g 1.预处理&#xff08;宏替换&#xff09; 2.编译&#xff08;生成汇编&#xff09; 3.汇编&#xff08;生成机器可识别代码&#xff09; 4.连…

鸿蒙Next开发真机调试签名申请流程

背景&#xff1a; 在学习鸿蒙next开发应用的初期总是会遇到一堆的问题&#xff0c;毕竟鸿蒙next开发不管是他的ArKTS语言还是他的开发工具DevEco Studio都还在起步阶段&#xff0c;就像当初的Android起步一样&#xff0c;总会有资料不足的一些问题。就比如我们学习下载完DevEco…

[yolov11改进系列]基于yolov11引入上下文锚点注意力CAA的python源码+训练源码

【CAA介绍】 本文记录的是基于CAA注意力模块的RT-DETR目标检测改进方法研究。在远程遥感图像或其他大尺度变化的图像中目标检测任务中&#xff0c;为准确提取其长距离上下文信息&#xff0c;需要解决大目标尺度变化和多样上下文信息时的不足的问题。CAA能够有效捕捉长距离依赖…

【linux】全志Tina预编译一个so库文件到根文件系统/usr/lib/下

一、sdk中新建文件夹 路径&#xff1a; V:\t113\work3\t113\openwrt\package\feeds\libs\md5util md5util为需要注入的库文件夹。 文件结构 libs md5util files libmd5util.so makefile etc.. 二、编写makefile include $(TOPDIR)/rules.mkPKG_NAME : md5util PKG_VERSIO…

C# 类和继承(成员访回修饰符)

成员访回修饰符 本章之前的两节阐述了类的可访问性。对类的可访问性&#xff0c;只有两种修饰符&#xff1a;internal和public。 本节阐述成员的可访问性。类的可访问性描述了类的可见性&#xff1b;成员的可访问性描述了类成员的可 见性。 声明在类中的每个成员对系统的不同…

Linux-文件管理及归档压缩

1.根下的目录作用说明&#xff1a; /&#xff1a;Linux系统中所有的文件都在根下/bin&#xff1a;(二进制命令目录)存放常用的用户命令/boot&#xff1a;系统启动时的引导文件&#xff08;内核的引导配置文件&#xff0c;grub配置文件&#xff0c;内核配置文件&#xff09; 例…

微软认证考试科目众多?该如何选择?

在云计算、人工智能、数据分析等技术快速发展的今天&#xff0c;微软认证&#xff08;Microsoft Certification&#xff09;已成为IT从业者、开发者、数据分析师提升竞争力的重要凭证。但面对众多考试科目&#xff0c;很多人不知道如何选择。本文将详细介绍微软认证的考试方向、…

Dify工作流实践—根据word需求文档编写测试用例到Excel中

前言 这篇文章依赖到的操作可查阅我之前的文章&#xff1a; dify里的大模型是怎么添加进来的&#xff1a;在Windows本地部署Dify详细操作 flask 框架app.route()函数的开发和调用&#xff1a;PythonWeb开发框架—Flask工程创建和app.route使用详解 结构化提示词的编写&…

【LC实战派】小智固件编译

这篇写给立创吴总&#xff0c;是节前答应他配合git代码的说明&#xff1b;也给所有对小智感兴趣的小伙伴。 请多提意见&#xff0c;让这份文档更有价值 - 第一当然是拉取源码 - git clone https://github.com/78/xiaozhi-esp32.git 完成后&#xff0c;先查看固件中实际的…

jdbcTemplate.query备忘录

jdbcTemplate.query中使用全部字符串和参数注入&#xff0c; 查询速度为什么差距这么大 如何正确使用JdbcTemplate参数化查询 1、使用?占位符 String sql "SELECT * FROM users WHERE name LIKE ?"; List<User> users jdbcTemplate.query(sql,new Object[…

如何搭建Z-Blog PHP版本:详细指南

Z-Blog是一款功能强大且易于使用的博客平台&#xff0c;支持PHP和ASP两种环境。本文将重点介绍如何在PHP环境下搭建Z-Blog博客系统&#xff0c;帮助您快速上线自己的个人博客站点。 准备工作 1. 获取Z-Blog PHP版本 首先&#xff0c;访问Z-Blog官方网站下载最新版本的Z-Blog…

Github Copilot新特性:Copilot Spaces-成为某个主题的专家

概述 当今的工程团队都会面临知识碎片化的问题。关键的上下文分散在代码、文档和团队成员的头脑中&#xff0c;这使得他们很难在一个新的领域快速上手并完成工作。Copilot Spaces 通过集中您的项目上下文解决了这个问题&#xff0c;因此 Copilot 可以根据您的工作提供更智能、…

攻防世界-XCTF-Web安全最佳刷题路线

每次写序都是最烦恼的&#xff0c;都不知道写什么&#xff0c;CTF是团队竞赛&#xff0c;有很多分支&#xff08;Web安全&#xff0c;密码学&#xff0c;杂项&#xff0c;Pwn&#xff0c;逆向&#xff0c;安卓&#xff09;&#xff0c;可以每个领域都涉猎&#xff0c;或许感觉那…

t021-高校物品捐赠管理系统【包含源码材料!!!!】

视频演示地址 摘 要 传统办法管理信息首先需要花费的时间比较多&#xff0c;其次数据出错率比较高&#xff0c;而且对错误的数据进行更改也比较困难&#xff0c;最后&#xff0c;检索数据费事费力。因此&#xff0c;在计算机上安装高校物品捐赠管理系统软件来发挥其高效地信息…

设计模式——面向对象设计六大原则

摘要 本文详细介绍了设计模式中的六大基本原则&#xff0c;包括单一职责原则、开放封闭原则、里氏替换原则、接口隔离原则、依赖倒置原则和合成复用原则。每个原则都通过定义、理解、示例三个部分进行阐述&#xff0c;旨在帮助开发者提高代码的可维护性和灵活性。通过具体代码…

Python制作史莱姆桌面宠物!可爱的

史莱姆桌面宠物 一个可爱的桌面史莱姆宠物&#xff0c;它会在您的任务栏上移动并提供可视化设置界面。 这里写目录标题 史莱姆桌面宠物功能特点安装与运行直接运行方式创建可执行文件 使用说明自定义GIF说明打包说明开源地址 功能特点 可爱的史莱姆在任务栏上自动移动支持…