React移动端侧边栏刷新状态重置问题及解决方案咨询
解决移动端侧边栏刷新恢复显示、桌面端默认展开的问题
我实现了可隐藏/显示的侧边栏菜单,但现在遇到两个问题:
- 移动端隐藏侧边栏后刷新页面,侧边栏会恢复初始显示状态
- 希望桌面端默认保持侧边栏展开
我考虑用useEffect修改初始状态,但不确定是否可行。以下是我的ContextProvider.js和Sidebar组件代码:
ContextProvider.js
import { useState, createContext, useContext, useEffect } from "react"; const StateContext = createContext() const initialState = { cart: false, notification: false, searchBar: false, } export const ContextProvider = ({ children }) => { const [activeMenu, setActiveMenu] = useState(true) const [isClicked, setIsClicked] = useState(initialState) const [screenSize, setScreenSize] = useState(undefined) const [currentColor, setCurrentColor] = useState('#bf9a8e') const [currentMode, setCurrentMode] = useState('Light') const [isOpen, setIsOpen] = useState(false) const [currentIndex, setCurrentIndex] = useState(0) const setMode = (e) => { setCurrentMode(e.target.value); localStorage.setItem('themeMode', e.target.value); }; const setColor = (color) => { setCurrentColor(color); localStorage.setItem('colorMode', color); }; const handleClick = (clicked) => { setIsClicked({...initialState, [clicked]: true}) } const slideLeft = () => { let slider = document.getElementById('slider-2') slider.scrollLeft = slider.scrollLeft - 500 } const slideRight = () => { let slider = document.getElementById('slider-2') slider.scrollLeft = slider.scrollLeft + 500 } return ( <StateContext.Provider value={{ activeMenu, setActiveMenu, isClicked, setIsClicked, handleClick, screenSize, setScreenSize, currentColor, currentMode, setColor, setMode, isOpen, setIsOpen, slideLeft, slideRight, currentIndex, setCurrentIndex, }}> {children} </StateContext.Provider> ) } export const useStateContext = () => useContext(StateContext)
Sidebar组件
function Sidebar() { const { activeMenu, setActiveMenu, screenSize, currentColor } = useStateContext(); const handleCloseSideBar = () => { if (activeMenu !== undefined && screenSize <= 900) { setActiveMenu(false); } }; const activeLink = 'flex items-center gap-5 pl-4 pt-3 pb-2.5 text-gray-100 dark:text-white rounded-lg bg-slate-700 text-md m-2' const normalLink = 'flex items-center gap-5 pl-4 pt-3 pb-2.5 rounded-lg text-md dark:text-gray-300 text-gray-500 dark:hover:text-gray-600 m-2 hover:bg-gray-200'; return ( <div className="mr-4 ml-4 mt-3 scrollbar-hide h-screen md:overflow-hidden overflow-auto md:hover:overflow-auto pb-10"> {/* LOGO SECTION */} {activeMenu && ( <> <div className="flex text-slate-700 dark:text-white justify-between"> <Link to="/" onClick={handleCloseSideBar} className="flex text-xl font-extrabold items-center gap-3 ml-4 mt-4 tracking-tight" > <GiPlanetCore/> <span>BaIlEyWoRld</span> </Link> <button type="button" onClick={() => setActiveMenu((prevActiveMenu) => !prevActiveMenu)} className="text-2xl p-3 mt-4 block sm:hidden" > <MdClose /> </button> </div> <div className="mt-10"> {links.map((item) => ( <div key={item.title}> <p className="text-gray-400 dark:text-gray-200 font-semibold uppercase m-4 mt-4"> {item.title} </p> {item.links.map((link) => ( <NavLink to={`/${link.name}`} key={link.name} onClick={handleCloseSideBar} className={({isActive}) => isActive ? activeLink : normalLink } style={({isActive}) => ({backgroundColor: isActive ? currentColor : ''})} > {link.icon} <span>{link.name}</span> </NavLink> ))} </div> ))} </div> </> )} </div> ); } export default Sidebar
解决方案
核心思路是用localStorage持久化移动端侧边栏状态,同时结合屏幕尺寸判断,强制桌面端始终展开侧边栏。具体修改如下:
修改后的ContextProvider.js
import { useState, createContext, useContext, useEffect } from "react"; const StateContext = createContext() const initialState = { cart: false, notification: false, searchBar: false, } export const ContextProvider = ({ children }) => { // 初始化状态:桌面端直接展开,移动端读取本地存储,默认展开 const getInitialActiveMenu = () => { const saved = localStorage.getItem('activeMenu'); const isDesktop = typeof window !== 'undefined' && window.innerWidth > 900; return isDesktop ? true : (saved ? JSON.parse(saved) : true); } const [activeMenu, setActiveMenu] = useState(getInitialActiveMenu) const [isClicked, setIsClicked] = useState(initialState) // 初始化屏幕尺寸,兼容服务端渲染场景 const [screenSize, setScreenSize] = useState(typeof window !== 'undefined' ? window.innerWidth : undefined) const [currentColor, setCurrentColor] = useState('#bf9a8e') const [currentMode, setCurrentMode] = useState('Light') const [isOpen, setIsOpen] = useState(false) const [currentIndex, setCurrentIndex] = useState(0) // 监听屏幕尺寸变化,桌面端强制展开侧边栏 useEffect(() => { const handleResize = () => { const width = window.innerWidth; setScreenSize(width); if (width > 900) { setActiveMenu(true); localStorage.setItem('activeMenu', JSON.stringify(true)); } } window.addEventListener('resize', handleResize); return () => window.removeEventListener('resize', handleResize); }, []) // 自定义状态更新函数,仅移动端保存状态到本地存储 const customSetActiveMenu = (value) => { setActiveMenu(value); if (screenSize <= 900) { localStorage.setItem('activeMenu', JSON.stringify(value)); } } const setMode = (e) => { setCurrentMode(e.target.value); localStorage.setItem('themeMode', e.target.value); }; const setColor = (color) => { setCurrentColor(color); localStorage.setItem('colorMode', color); }; const handleClick = (clicked) => { setIsClicked({...initialState, [clicked]: true}) } const slideLeft = () => { let slider = document.getElementById('slider-2') slider.scrollLeft = slider.scrollLeft - 500 } const slideRight = () => { let slider = document.getElementById('slider-2') slider.scrollLeft = slider.scrollLeft + 500 } return ( <StateContext.Provider value={{ activeMenu, setActiveMenu: customSetActiveMenu, // 替换为自定义更新函数 isClicked, setIsClicked, handleClick, screenSize, setScreenSize, currentColor, currentMode, setColor, setMode, isOpen, setIsOpen, slideLeft, slideRight, currentIndex, setCurrentIndex, }}> {children} </StateContext.Provider> ) } export const useStateContext = () => useContext(StateContext)
优化后的Sidebar组件(可选)
简化侧边栏关闭逻辑:
function Sidebar() { const { activeMenu, setActiveMenu, screenSize, currentColor } = useStateContext(); const handleCloseSideBar = () => { // 移动端点击链接后自动关闭侧边栏 if (screenSize <= 900) { setActiveMenu(false); } }; const activeLink = 'flex items-center gap-5 pl-4 pt-3 pb-2.5 text-gray-100 dark:text-white rounded-lg bg-slate-700 text-md m-2' const normalLink = 'flex items-center gap-5 pl-4 pt-3 pb-2.5 rounded-lg text-md dark:text-gray-300 text-gray-500 dark:hover:text-gray-600 m-2 hover:bg-gray-200'; return ( <div className="mr-4 ml-4 mt-3 scrollbar-hide h-screen md:overflow-hidden overflow-auto md:hover:overflow-auto pb-10"> {/* LOGO SECTION */} {activeMenu && ( <> <div className="flex text-slate-700 dark:text-white justify-between"> <Link to="/" onClick={handleCloseSideBar} className="flex text-xl font-extrabold items-center gap-3 ml-4 mt-4 tracking-tight" > <GiPlanetCore/> <span>BaIlEyWoRld</span> </Link> <button type="button" onClick={() => setActiveMenu((prevActiveMenu) => !prevActiveMenu)} className="text-2xl p-3 mt-4 block sm:hidden" > <MdClose /> </button> </div> <div className="mt-10"> {links.map((item) => ( <div key={item.title}> <p className="text-gray-400 dark:text-gray-200 font-semibold uppercase m-4 mt-4"> {item.title} </p> {item.links.map((link) => ( <NavLink to={`/${link.name}`} key={link.name} onClick={handleCloseSideBar} className={({isActive}) => isActive ? activeLink : normalLink } style={({isActive}) => ({backgroundColor: isActive ? currentColor : ''})} > {link.icon} <span>{link.name}</span> </NavLink> ))} </div> ))} </div> </> )} </div> ); } export default Sidebar
内容的提问来源于stack exchange,提问作者andrea siano
相关产品推荐
相关产品推荐

