You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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>
  );
}

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属性始终是初始挂载时的旧值。

修复步骤

  1. 监听LocalStorage变化:添加storage事件监听,跨标签页更新时同步state;同时定期检查当前页面的LocalStorage,处理同页面内的更新。
  2. 优化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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.30 04:45:04