React漫威API应用Menu组件未同步Local Storage值问题
问题描述
我基于Marvel API开发了一款可搜索英雄、漫画、事件、系列作品及创作者并展示其信息的应用。目前所有功能均正常,但Menu.jsx组件无法同步Local Storage中的值。例如,当我搜索英雄"Wolverine"时,Local Storage中的"heroName"变量会被设置为"Wolverine",但点击logo链接或"Heroes"链接时,页面无法加载该"heroName"对应的内容。
App.jsx代码
export default function App() { return ( <HashRouter> <Menu /> <Routes> {/* Heroes */} <Route path={'/:hName'} element={<Home />} /> <Route exact path={'/characters/:id/comics'} element={<HeroComics />} /> <Route exact path={'/characters/:id/events'} element={<HeroEvents />} /> <Route exact path={'/characters/:id/series'} element={<HeroSeries />} /> <Route exact path={'/characters/:id/stories'} element={<HeroStories />} /> {/* Comics */} <Route exact path={'/comics'} element={<ComicsPage />} /> {/* Events */} <Route exact path={'/events'} element={<EventsPage />} /> {/* Series */} <Route exact path={'/series'} element={<SeriesPage />} /> {/* Stories */} <Route exact path={'/stories'} element={<StoriesPage />} /> {/* Creators */} <Route path={'/creators/:cName'} element={<CreatorsPage />} /> <Route exact path={'/creators/:id/comics'} element={<CreatorComics />} /> <Route exact path={'/creators/:id/events'} element={<CreatorEvents />} /> <Route exact path={'/creators/:id/series'} element={<CreatorSeries />} /> <Route exact path={'/creators/:id/stories'} element={<CreatorStories />} /> </Routes> <Footer /> </HashRouter> ); }
Menu.jsx代码
export default function Menu() { const [heroName, setHeroName] = useState(""); const [creatorName, setCreatorName] = useState(""); useEffect(() => { setHeroName(localStorage.getItem('heroName')); setCreatorName(localStorage.getItem('creatorName')); }, []); return ( <Navbar expand="lg" sticky="top" className="nav-bar"> <Container> <Navbar.Brand as={Link} to={`/${heroName}`} className="logo-text">Marvel App</Navbar.Brand> <Navbar.Toggle aria-controls="responsive-navbar-nav" /> <Navbar.Collapse id="responsive-navbar-nav"> <Nav className="ml-auto"> <Nav.Link as={Link} to={`/${heroName}`}>Heroes</Nav.Link> <Nav.Link as={Link} to="/comics">Comics</Nav.Link> <Nav.Link as={Link} to="/events">Events</Nav.Link> <Nav.Link as={Link} to="/series">Series</Nav.Link> {/* <Nav.Link as={Link} to="/stories">Stories</Nav.Link> */} <Nav.Link as={Link} to={`/creators/${creatorName}`}>Creators</Nav.Link> </Nav> </Navbar.Collapse> </Container> </Navbar> ) }
Home.jsx中LocalStorage相关代码
useEffect(() => { localStorage.setItem('heroName', hName); const heroName = localStorage.getItem('heroName'); setHeroName(heroName); handleClick(heroName); }, []); const handleShow = async (heroID) => { setShow(true); try { let data = await fetchCharactersByCharacterID(heroID); setHero(data.data.results); } catch (err) { return err; } } const handleClick = async (heroName) => { try { let data = await fetchCharacters(heroName); setHeroes(data.data.results); localStorage.setItem('heroName', heroName); navigate(`/${heroName}`); } catch (err) { return err; } }
解决方案
问题根源
Menu组件的useEffect仅在组件挂载时执行一次,后续LocalStorage更新时,组件不会主动重新读取最新值,导致链接的to属性始终是初始挂载时的旧值。
修复步骤
- 监听LocalStorage变化:添加
storage事件监听,跨标签页更新时同步state;同时定期检查当前页面的LocalStorage,处理同页面内的更新。 - 优化Home组件逻辑:移除重复的LocalStorage设置,确保state与存储值同步。
修改后的Menu.jsx
export default function Menu() { // 初始化时直接读取LocalStorage值 const [heroName, setHeroName] = useState(localStorage.getItem('heroName') || ""); const [creatorName, setCreatorName] = useState(localStorage.getItem('creatorName') || ""); useEffect(() => { // 监听跨标签页的storage变化 const handleStorageChange = (e) => { if (e.key === 'heroName') { setHeroName(e.newValue); } else if (e.key === 'creatorName') { setCreatorName(e.newValue); } }; window.addEventListener('storage', handleStorageChange); return () => window.removeEventListener('storage', handleStorageChange); }, []); // 监听同页面内的LocalStorage更新 useEffect(() => { const checkStorage = () => { const storedHero = localStorage.getItem('heroName'); const storedCreator = localStorage.getItem('creatorName'); if (storedHero !== heroName) setHeroName(storedHero); if (storedCreator !== creatorName) setCreatorName(storedCreator); }; // 定期检查,可根据需求调整间隔 const interval = setInterval(checkStorage, 500); return () => clearInterval(interval); }, [heroName, creatorName]); return ( <Navbar expand="lg" sticky="top" className="nav-bar"> <Container> <Navbar.Brand as={Link} to={`/${heroName || ""}`} className="logo-text">Marvel App</Navbar.Brand> <Navbar.Toggle aria-controls="responsive-navbar-nav" /> <Navbar.Collapse id="responsive-navbar-nav"> <Nav className="ml-auto"> <Nav.Link as={Link} to={`/${heroName || ""}`}>Heroes</Nav.Link> <Nav.Link as={Link} to="/comics">Comics</Nav.Link> <Nav.Link as={Link} to="/events">Events</Nav.Link> <Nav.Link as={Link} to="/series">Series</Nav.Link> {/* <Nav.Link as={Link} to="/stories">Stories</Nav.Link> */} <Nav.Link as={Link} to={`/creators/${creatorName || ""}`}>Creators</Nav.Link> </Nav> </Navbar.Collapse> </Container> </Navbar> ) }
优化后的Home.jsx逻辑
// 原useEffect修改为,避免重复设置LocalStorage useEffect(() => { if (hName) { setHeroName(hName); handleClick(hName); } }, [hName]); // 保持handleClick逻辑,同时同步state const handleClick = async (heroName) => { try { let data = await fetchCharacters(heroName); setHeroes(data.data.results); localStorage.setItem('heroName', heroName); setHeroName(heroName); // 同步组件内state navigate(`/${heroName}`); } catch (err) { return err; } }
额外说明
storage事件仅在跨标签页更新LocalStorage时触发,所以添加定时器定期检查当前页面的存储值,确保同页面内修改后Menu能及时更新。- 给
to属性添加|| ""处理空值,避免生成无效路由。
内容的提问来源于stack exchange,提问作者Julian
相关产品推荐
相关产品推荐

