2026-06-18 20:13:18 +08:00
|
|
|
|
import { useEffect, useState } from 'react'
|
|
|
|
|
|
import { fetchWeather, type WeatherResult } from '../api/weather'
|
2026-06-20 20:45:39 +08:00
|
|
|
|
import { locate } from '../lib/amap'
|
2026-06-18 20:13:18 +08:00
|
|
|
|
|
|
|
|
|
|
export interface WeatherState {
|
|
|
|
|
|
loading: boolean
|
|
|
|
|
|
/** 后端返回的结果(含限流/未配置等业务态);网络错误时为 null */
|
|
|
|
|
|
data: WeatherResult | null
|
|
|
|
|
|
/** 网络层错误(后端不可达) */
|
|
|
|
|
|
error: boolean
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 拉取天气。默认每 30 分钟刷新一次(后端有 10 分钟缓存与每日限流,
|
|
|
|
|
|
* 所以前端即便频繁挂载也不会浪费上游额度)。
|
|
|
|
|
|
*/
|
|
|
|
|
|
export function useWeather(intervalMs = 30 * 60 * 1000): WeatherState {
|
|
|
|
|
|
const [state, setState] = useState<WeatherState>({ loading: true, data: null, error: false })
|
|
|
|
|
|
|
|
|
|
|
|
useEffect(() => {
|
|
|
|
|
|
let cancelled = false
|
|
|
|
|
|
|
|
|
|
|
|
const load = async () => {
|
|
|
|
|
|
try {
|
2026-06-20 20:45:39 +08:00
|
|
|
|
// 先前端定位(未配 JS Key 或失败 → null,后端用 IP 兜底)。locate 不抛错。
|
|
|
|
|
|
const geo = await locate()
|
|
|
|
|
|
const data = await fetchWeather(geo ?? undefined)
|
2026-06-18 20:13:18 +08:00
|
|
|
|
if (!cancelled) setState({ loading: false, data, error: false })
|
|
|
|
|
|
} catch {
|
|
|
|
|
|
if (!cancelled) setState({ loading: false, data: null, error: true })
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
load()
|
|
|
|
|
|
const id = setInterval(load, intervalMs)
|
|
|
|
|
|
return () => {
|
|
|
|
|
|
cancelled = true
|
|
|
|
|
|
clearInterval(id)
|
|
|
|
|
|
}
|
|
|
|
|
|
}, [intervalMs])
|
|
|
|
|
|
|
|
|
|
|
|
return state
|
|
|
|
|
|
}
|