From 4c6eaa63903aaaf3346437cbfb02a21409eb0490 Mon Sep 17 00:00:00 2001 From: "2569718930@qq.com" <2569718930@qq.com> Date: Sun, 17 May 2026 21:50:20 +0800 Subject: [PATCH] feat: add useAiCityForecast hook to manage streamed AI city weather forecasting logic --- .../scan-terminal/use-ai-city-forecast.ts | 26 +++++++++++-------- 1 file changed, 15 insertions(+), 11 deletions(-) diff --git a/frontend/components/dashboard/scan-terminal/use-ai-city-forecast.ts b/frontend/components/dashboard/scan-terminal/use-ai-city-forecast.ts index f06473db..a9bdc635 100644 --- a/frontend/components/dashboard/scan-terminal/use-ai-city-forecast.ts +++ b/frontend/components/dashboard/scan-terminal/use-ai-city-forecast.ts @@ -1,6 +1,6 @@ "use client"; -import { useCallback, useEffect, useMemo, useState } from "react"; +import { useCallback, useEffect, useMemo, useRef, useState } from "react"; import { scanTerminalClient } from "@/components/dashboard/scan-terminal/scan-terminal-client"; import type { AiCityForecastState } from "@/components/dashboard/scan-terminal/types"; import type { CityDetail } from "@/lib/dashboard-types"; @@ -33,6 +33,12 @@ export function useAiCityForecast({ report: string; }) { const [aiRefreshToken, setAiRefreshToken] = useState(0); + // Keep refs to the latest detail/report so the effect closure always reads + // current values without needing to list unstable object refs as deps. + const detailRef = useRef(detail); + const reportRef = useRef(report); + detailRef.current = detail; + reportRef.current = report; const aiForecastKey = useMemo( () => buildAiCityForecastKey({ detail, detailCityName, locale, report }), [detail, detailCityName, locale, report], @@ -67,19 +73,19 @@ export function useAiCityForecast({ const loadingState = buildAiCityLoadingForecastState({ cacheKey, - detail, + detail: detailRef.current, isEn, - report, + report: reportRef.current, }); setAiForecast(loadingState); const softTimeoutId = window.setTimeout(() => { if (cancelled || resolved) return; const fallbackState = buildAiCityErrorForecastState({ cacheKey, - detail, + detail: detailRef.current, error: "ai_soft_timeout_fallback", isEn, - report, + report: reportRef.current, }); setAiForecast(fallbackState); }, AI_CITY_READ_SOFT_TIMEOUT_MS); @@ -106,10 +112,10 @@ export function useAiCityForecast({ window.clearTimeout(softTimeoutId); const readyState = buildAiCityReadyForecastState({ cacheKey, - detail, + detail: detailRef.current, isEn, payload, - report, + report: reportRef.current, }); if (!cancelled) { setAiForecast(readyState); @@ -120,10 +126,10 @@ export function useAiCityForecast({ window.clearTimeout(softTimeoutId); const errorState = buildAiCityErrorForecastState({ cacheKey, - detail, + detail: detailRef.current, error, isEn, - report, + report: reportRef.current, }); if (!cancelled) { setAiForecast(errorState); @@ -136,12 +142,10 @@ export function useAiCityForecast({ }, [ aiForecastKey, aiRefreshToken, - detail, detailCityName, enabled, isEn, locale, - report, ]); const refreshAiForecast = useCallback(() => {