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

Next.js中useCity自定义Hook跨页面状态不更新问题排查与解决

问题原因与解决方案

问题根源

你写的useCity自定义Hook本质是状态逻辑复用,而非状态共享。每个组件(包括不同页面的组件)调用useCity时,都会独立执行一次Hook内部的useState,生成完全隔离的状态实例。也就是说,/changeCity页面修改的是该页面自己的city状态,/city页面的city状态是另一套独立的变量,两者毫无关联,自然不会同步更新。

正确解决方案

下面是几种适用于Next.js项目的可行方案:

方案1:React Context API(原生方案,无额外依赖)

通过Context实现全局状态共享,所有页面共享同一状态实例:

  1. 创建Context文件:contexts/CityContext.ts
import { createContext, useContext, useState, ReactNode } from 'react';

type CityContextType = {
  city: string | null;
  setCity: (city: string | null) => void;
};

const CityContext = createContext<CityContextType | undefined>(undefined);

export function CityProvider({ children }: { children: ReactNode }) {
  const [city, setCity] = useState<string | null>(null);

  return (
    <CityContext.Provider value={{ city, setCity }}>
      {children}
    </CityContext.Provider>
  );
}

export function useCity() {
  const context = useContext(CityContext);
  if (!context) {
    throw new Error('useCity must be used within a CityProvider');
  }
  return context;
}
  1. 在_app.tsx中包裹所有页面:
import { AppProps } from 'next/app';
import { CityProvider } from '../contexts/CityContext';

export default function App({ Component, pageProps }: AppProps) {
  return (
    <CityProvider>
      <Component {...pageProps} />
    </CityProvider>
  );
}
  1. 页面中直接调用useCity即可共享状态:
// /pages/changeCity.tsx
import { useCity } from '../contexts/CityContext';
import { useEffect } from 'react';

export default function ChangeCity() {
  const { setCity } = useCity();

  useEffect(() => {
    setCity('Beijing');
  }, [setCity]);

  return <div>Changed city</div>;
}
// /pages/city.tsx
import { useCity } from '../contexts/CityContext';
import { useEffect } from 'react';

export default function City() {
  const { city } = useCity();

  useEffect(() => {
    console.log('Current city:', city);
  }, [city]);

  return <div>Current city: {city}</div>;
}

方案2:状态管理库(比如Zustand,轻量易用)

如果项目需要更复杂的状态管理,Zustand是个不错的选择:

  1. 安装Zustand:
npm install zustand
  1. 创建store文件:stores/cityStore.ts
import { create } from 'zustand';

type CityStore = {
  city: string | null;
  setCity: (city: string | null) => void;
};

export const useCityStore = create<CityStore>((set) => ({
  city: null,
  setCity: (newCity) => set({ city: newCity }),
}));
  1. 页面中使用:
// /pages/changeCity.tsx
import { useCityStore } from '../stores/cityStore';
import { useEffect } from 'react';

export default function ChangeCity() {
  const setCity = useCityStore((state) => state.setCity);

  useEffect(() => {
    setCity('Shanghai');
  }, [setCity]);

  return <div>Changed city</div>;
}
// /pages/city.tsx
import { useCityStore } from '../stores/cityStore';
import { useEffect } from 'react';

export default function City() {
  const city = useCityStore((state) => state.city);

  useEffect(() => {
    console.log('Current city:', city);
  }, [city]);

  return <div>Current city: {city}</div>;
}

方案3:本地存储(结合useState,适合需要持久化的场景)

如果需要状态在页面刷新后依然保留,可以结合localStorage:

修改useCity.ts:

import { useState, useEffect } from 'react';

export function useCity() {
  // 初始化时从localStorage读取
  const [city, setCity] = useState<string | null>(() => {
    if (typeof window !== 'undefined') {
      return localStorage.getItem('city') || null;
    }
    return null;
  });

  // 修改状态时同步到localStorage
  useEffect(() => {
    if (city !== null) {
      localStorage.setItem('city', city);
    } else {
      localStorage.removeItem('city');
    }
  }, [city]);

  // 监听其他页面修改localStorage的事件(跨标签页同步)
  useEffect(() => {
    function handleStorageChange(e: StorageEvent) {
      if (e.key === 'city') {
        setCity(e.newValue || null);
      }
    }

    window.addEventListener('storage', handleStorageChange);
    return () => window.removeEventListener('storage', handleStorageChange);
  }, []);

  return { city, setCity };
}

这种方案下,页面跳转后会读取localStorage的最新值,跨标签页也能同步状态,但注意Next.js服务端渲染时要判断window是否存在。

内容的提问来源于stack exchange,提问作者Sergey Kamalian

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.19 14:57:44