React Router跨页面导航时如何保留侧边栏已选筛选条件
侧边栏筛选参数跨页面留存实现方案
筛选值跨页面重置的核心原因有两个:要么是侧边栏组件被放在各个路由页面内单独引入,切路由时组件卸载重挂载,内部state被清空;要么是筛选状态存在路由子组件的作用域里,路由切换时组件重渲染导致状态重置。
下面是3种可直接落地的实现方案,按适配场景优先级排序:
方案1:全局状态托管(最适配当前路由结构,优先选)
把筛选状态抽到路由组件树之上的全局作用域,保证路由切换时状态存储容器不会被销毁,同时把侧边栏挪到路由外层全局只渲染一次,从根源上避免组件重复挂载导致的状态丢失。
如果项目没装额外的全局状态库,用React自带的Context就能实现,改动最小:
import { createContext, useState, useCallback, useContext } from 'react' // 先创建筛选上下文 const FilterContext = createContext({ deviceFilter: '', timeRangeFilter: '', setFilterValue: () => {} }) // 调整App根组件结构 export default function App() { const [filterParams, setFilterParams] = useState({ deviceFilter: '', // 对应第一个下拉筛选项默认值 timeRangeFilter: '' // 对应第二个下拉筛选项默认值 }) const setFilterValue = useCallback((filterKey, value) => { setFilterParams(prev => ({...prev, [filterKey]: value})) }, []) return ( <ThemeProvider theme={theme} style={{width: '100vw', height: '100vh'}}> {/* 上下文放在路由外层,路由切换不会销毁状态 */} <FilterContext.Provider value={{...filterParams, setFilterValue}}> <Header/> <BrowserRouter> {/* 侧边栏挪到Routes外层,全局只挂载1次,不要在每个路由页面重复引入 */} <GlobalSidebar /> <Routes> <Route exact path='/test' element={<Test filterKey='device'/>}/> <Route exact path='/devices' element={<DeviceList/>}/> <Route exact path='/devices/:deviceId' element={<DeviceId/>}/> <Route exact path='/devices/:deviceId/:userId' element={<UserId/>}/> <Route exact path='/devices/:deviceId/:userId/:sessionId' element={<SessionId/>}/> <Route exact path='/devices/:deviceId/:userId/:sessionId/:pocketId' element={<PocketId/>}/> <Route path="*" element={<Navigate to="/devices" replace />}/> </Routes> <Footer/> </BrowserRouter> </FilterContext.Provider> </ThemeProvider> ) } // 侧边栏组件直接读写全局上下文的状态 function GlobalSidebar() { const {deviceFilter, timeRangeFilter, setFilterValue} = useContext(FilterContext) return ( <div className="sidebar"> <select value={deviceFilter} onChange={e => setFilterValue('deviceFilter', e.target.value)} > {/* 筛选项内容 */} </select> <select value={timeRangeFilter} onChange={e => setFilterValue('timeRangeFilter', e.target.value)} > {/* 筛选项内容 */} </select> </div> ) }
如果项目已经在用Redux、Zustand这类全局状态库,直接把筛选状态存到全局store里即可,不用额外写Context。
方案2:URL Query参数持久化(适合需要分享筛选状态的场景)
把选中的筛选值同步到URL的查询参数里,路由跳转时自动带上当前query参数,侧边栏初始化时直接从URL读取选中值。
用react-router自带的useSearchParams钩子就能实现,好处是刷新页面、把链接发给其他用户时,筛选状态也能保留。需要注意路由跳转时不要丢失query参数,跳转时要带上当前的search字符串:
const [searchParams] = useSearchParams() const navigate = useNavigate() // 跳转示例 navigate(`/devices/xxx/xxx?${searchParams.toString()}`)
方案3:本地存储持久化(适合长期记住用户偏好的场景)
筛选值变化时同步存入localStorage/sessionStorage,侧边栏初始化时优先读本地存储的默认值。实现简单,但要注意必须把侧边栏放在路由外层全局只渲染一次,否则切页面时会出现值短暂重置的闪屏:
function GlobalSidebar() { const [deviceFilter, setDeviceFilter] = useState(() => localStorage.getItem('deviceFilter') || '') const [timeRangeFilter, setTimeRangeFilter] = useState(() => localStorage.getItem('timeRangeFilter') || '') useEffect(() => { localStorage.setItem('deviceFilter', deviceFilter) }, [deviceFilter]) useEffect(() => { localStorage.setItem('timeRangeFilter', timeRangeFilter) }, [timeRangeFilter]) // 下拉渲染逻辑和之前一致 }
以上三种方案可以组合使用,比如全局状态+URL参数同步,兼顾开发便捷性和分享需求。
内容的提问来源于stack exchange,提问作者Paul
相关产品推荐
相关产品推荐

