如何使用浏览器历史记录在条件渲染组件间实现前进后退导航
问题原因
你当前的组件显隐状态仅存储在React内部的useState中,没有和浏览器的历史栈绑定,浏览器感知不到你在当前页面切换了组件,因此点击返回按钮会直接退出当前页面。另外你提供的示例代码里两个子组件都用showChild1控制显隐属于笔误,下面的方案默认按showChild1控制Child1、showChild2控制Child2实现。
方案1:使用原生History API实现(无需引入额外路由库)
通过浏览器自带的History API手动把组件状态写入历史栈,同时监听前进后退事件同步React状态即可:
import { useEffect, useState } from 'react' const ParentComponent = () => { const [showChild1, setShowChild1] = useState(false) const [showChild2, setShowChild2] = useState(false) useEffect(() => { // 监听浏览器前进后退事件,同步状态 const handlePopState = (e) => { const historyState = e.state if (!historyState) { // 回到当前页面无状态的初始阶段,重置所有子组件显隐 setShowChild1(false) setShowChild2(false) return } setShowChild1(historyState.activeChild === 'child1' || historyState.activeChild === 'child2') setShowChild2(historyState.activeChild === 'child2') } window.addEventListener('popstate', handlePopState) return () => window.removeEventListener('popstate', handlePopState) }, []) // 切换显示Child1 const openChild1 = () => { // 把当前状态写入历史栈 window.history.pushState({ activeChild: 'child1' }, '') setShowChild1(true) setShowChild2(false) } // 切换显示Child2 const openChild2 = () => { window.history.pushState({ activeChild: 'child2' }, '') setShowChild2(true) } return ( <div> <button onClick={openChild1}>打开Child1</button> <button onClick={openChild2}>打开Child2</button> <ParentComponent> {showChild1 && <Child1 />} {showChild2 && <Child2 />} </ParentComponent> </div> ) }
方案2:使用React Router等路由库实现(更适合正式项目)
如果项目本身已经接入路由库,直接把子组件对应到不同的子路由即可,路由库会自动帮你管理历史栈,不需要手动处理事件:
import { Routes, Route, useNavigate } from 'react-router-dom' const ParentComponent = () => { const navigate = useNavigate() return ( <div> <button onClick={() => navigate('child1')}>打开Child1</button> <button onClick={() => navigate('child2')}>打开Child2</button> <Routes> <Route path="child1" element={<Child1 />} /> <Route path="child2" element={<Child2 />} /> </Routes> </div> ) }
注意事项
- 如果你需要页面刷新后仍保留当前组件的展示状态,可以将组件标识存储在URL的hash或query参数中,页面初始化时直接从URL读取参数同步状态即可
history.pushState调用时不会触发popstate事件,仅用户点击浏览器前进/后退按钮、或手动调用history.back()/history.go()等方法时才会触发该事件
内容的提问来源于stack exchange,提问作者Shahir Riaz
相关产品推荐
相关产品推荐

