使用useSearchParams或router.replace拼接地址栏——解决tab标签页刷新状态丢失问题

问题描述

  • 日常我们使用tab标签页组件的时候,可以点击tab进行标签页的切换
  • 但是,假若我们刷新浏览器页面
  • tab标签页会恢复回到默认的高亮的tab
  • 但是,某些情况下,我们可能想要页面刷新后,依旧保持在当前高亮的tab标签页上
  • 本文通过react和vue的示例
  • 对应使用useSearchParams或router.replace将高亮的key拼接在地址栏上
  • 这样的话,当我们刷新网页的时候
  • 就会停留在我们当前的高亮tab页签上了

当然,也可以把高亮的tab存储在sessionStorage或者localStorage中,这里不赘述了,思想是一样的

效果图

  • 分析上图,初始状态,高亮的是Tab 1
  • 切换tab后,刷新,高亮状态保留

思路分析

React中的控制

  • 在React中,我们可以使用useSearchParams这个hook进行地址栏的查询参数的读取和修改
  • 如下基础用法
js 复制代码
import { useSearchParams } from 'react-router-dom';

function UserProfile() {
  // 获取查询参数对象和修改函数
  const [searchParams, setSearchParams] = useSearchParams();
  
  // 读取查询参数
  const name = searchParams.get('name'); // 获取 name 参数值
  const age = searchParams.get('age');   // 获取 age 参数值
  
  // 修改查询参数
  const handleUpdateParams = () => {
    setSearchParams({ name: 'Alice', age: '25' });
  };
  
  return (
    <div>
      <p>Name: {name} --- Age: {age}</p>
      <button onClick={handleUpdateParams}>
        Update Parameters
      </button>
    </div>
  );
}
  • 即,当页面didMounted的时候获取地址栏tab参数
  • 若有高亮的tab的key的值,就直接修改tab组件的高亮值(没有就指定默认的,比如默认高亮第一个)
  • 同时也要做searchParams的数据收集依赖监听,这样控制就可以做到当用户手动修改浏览器地址栏高亮tab的时候
  • 切换到对应的tab
  • 同时,当用户点击tab组件的时候,在onChange事件中,也要做对应浏览器地址栏参数的修改

两个控制:1. 修改地址栏tab高亮参数,要切换标签页;2. 点击tab组件,也要切换标签页,同时修改地址栏参数

React代码

tab数据:

js 复制代码
const items: TabsProps['items'] = [
  {
    key: '1',
    label: 'Tab 1',
    children: 'Content of Tab Pane 1',
  },
  {
    key: '2',
    label: 'Tab 2',
    children: 'Content of Tab Pane 2',
  },
  {
    key: '3',
    label: 'Tab 3',
    children: 'Content of Tab Pane 3',
  },
];

逻辑控制代码

js 复制代码
import React, { useState, useEffect } from 'react'
import { Tabs } from 'antd';
// useLocation用于获取完成地址栏信息,useSearchParams用于获取查询query信息,并返回可读写的searchParams对象
import { useSearchParams } from 'react-router-dom'
import type { TabsProps } from 'antd';

export default function TabStatus() {
  const [searchParams, setSearchParams] = useSearchParams()

  // 1. 默认高亮第一个tab
  const [activeKey, setActiveKey] = useState('1')

  useEffect(() => {
    // 2. 当页面didMounted的时候获取地址栏tab参数,并修改tab高亮
    const queryObj = Object.fromEntries(searchParams.entries())
    setActiveKey(queryObj.tab || '1')
    // 3. 后续当地址栏参数发生变化的时候,也去获取地址栏tab参数,并修改tab高亮
  }, [searchParams]) // 也可以只单独监听tab [searchParams.get('tab')] 不过最好提取出来

  const onChange = (key: string) => {
    console.log('onChange', key);
    // 4. 当tab高亮发生变化时,修改地址栏参数
    const queryObj = Object.fromEntries(searchParams.entries())
    queryObj['tab'] = key
    setSearchParams(queryObj)
    // setSearchParams({tab: key}) // 注意不要只修改tab把别的地址栏参数覆盖掉了
  };

  return (
    <div>
      <h2>TabStatus------刷新依旧保留当前tab状态</h2>
      <Tabs activeKey={activeKey} items={items} onChange={onChange} />
    </div>
  )
}

Vue中的控制

  • Vue就更加简单好控制了
  • 页面加载时,检查query参数中的tab值,如果有,则设置activeName为该值
  • 当tabChange的时候
  • 保留原本的query参数同时,再拼接tab参数,这样刷新页面后,仍然可以保持在当前tab页

Vue代码

html结构:

html 复制代码
<template>
  <div class="boxWrap">
    <h3>tab标签刷新页面,保留当前tab停留的状态页</h3>
    <br>
    <el-tabs v-model="activeName" @tab-change="tabChange">
      <el-tab-pane label="first" name="first">first</el-tab-pane>
      <el-tab-pane label="second" name="second">second</el-tab-pane>
      <el-tab-pane label="third" name="third">third</el-tab-pane>
      <el-tab-pane label="fourth" name="fourth">fourth</el-tab-pane>
    </el-tabs>
  </div>
</template>

js代码:

js 复制代码
<script setup>
import { ref, onMounted } from "vue";
import { useRoute, useRouter } from "vue-router";

const router = useRouter();
const route = useRoute();

const query = route.query;

// 默认第一个tab页
const activeName = ref("first");

onMounted(() => {
  /**
   * 页面加载时,检查query参数中的tab值,如果有,则设置activeName为该值
   * */
  if (query.tab) {
    activeName.value = query.tab;
  }
});

const tabChange = (tabName) => {
  /**
   * 保留原本的query参数同时,再拼接tab参数,这样刷新页面后,仍然可以保持在当前tab页
   * */
  router.replace({ query: { ...query, tab: tabName } });
};
</script>
  • 上述是基于React和Vue的路由提供的钩子进行的控制
  • 这些路由钩子的底层实际上也就是基于history.pushState和history.replaceState实现的
  • 当然,也可直接用原生js去书写

history.pushState更新地址栏

思想是一样的,这里不赘述,如下示例思路代码:

js 复制代码
// 不刷新页面拼接地址栏query参数【前提是本身就有 ?key=value 】
export const updateUrl = (q: string) => {
    var url = window.location.href; // 获取当前URL
    var newUrl = url + '&' + q; // 拼接新的URL q: name=tom
    history.pushState(null, '', newUrl); // 修改地址栏URL
}

updateUrl(`tab=${tab}`)
  • 同样的,比如页面中有一个dialog是打开的状态,我们也可以通过上述的控制
  • 保留dialog的打开状态(地址栏拼接对应的dialog的开启关闭的布尔值之类的)

A good memory is better than a bad pen. Record it down...

在线示例效果和代码仓库

欢迎Star呦😄😄😄

相关推荐
二哈喇子!5 分钟前
Vue 组件化开发
前端·javascript·vue.js
chxii28 分钟前
2.9 插槽
前端·javascript·vue.js
江城开朗的豌豆1 小时前
React表单控制秘籍:受控组件这样玩就对了!
前端·javascript·react.js
接着奏乐接着舞。2 小时前
如何在Vue中使用拓扑图功能
前端·javascript·vue.js
老华带你飞2 小时前
生产管理ERP系统|物联及生产管理ERP系统|基于SprinBoot+vue的制造装备物联及生产管理ERP系统设计与实现(源码+数据库+文档)
java·数据库·vue.js·论文·制造·毕设·生产管理erp系统
阳先森2 小时前
Vue3 Proxy 为何不直接返回target[key],选用Reflect
前端·vue.js
Casta-mere3 小时前
React SSR 水合问题
前端·react.js·前端框架·ssr
黄毛火烧雪下3 小时前
React 为什么要自定义 Hooks?
javascript·react.js·ecmascript
野区小女王4 小时前
React函数组件灵魂搭档:useEffect深度通关指南!
前端·react.js·前端框架
我是火山呀4 小时前
React+TypeScript代码注释规范指南
前端·react.js·typescript