React 中的 IP 地理位置:一个访问者位置组件
围绕 Watch-IP JavaScript SDK 构建一个可复用的 useVisitorGeo hook,涵盖真实 React 组件所需的加载、错误与取消处理——包括为什么该请求在开发环境下可能会触发两次。
开始之前
- 一个 Watch-IP 账户,以及一个公开可用的访问者密钥,其允许来源列表中包含您应用的来源(开发与生产环境均需包含)。
- React 18 或更高版本——下文描述的 StrictMode 双重调用行为仅出现在开发模式下。
- 已安装 @digitload/watch-ip-sdk(npm install @digitload/watch-ip-sdk)。
1. 构建一个 useVisitorGeo hook
将 SDK 的 getGeo() 调用封装进一个 hook 中,跟踪加载/结果/错误状态,并在组件于请求完成前卸载时取消该请求。
TypeScript
import { useEffect, useState } from "react";
import { WatchIP, type GeoResponse } from "@digitload/watch-ip-sdk";
const client = new WatchIP("YOUR_PUBLISHABLE_KEY");
export function useVisitorGeo() {
const [geo, setGeo] = useState<GeoResponse | null>(null);
const [status, setStatus] = useState<"loading" | "ready" | "error">("loading");
useEffect(() => {
const controller = new AbortController();
client
.getGeo({ signal: controller.signal })
.then((result) => {
setGeo(result);
setStatus("ready");
})
.catch((err) => {
if (err.name !== "AbortError") setStatus("error");
});
return () => controller.abort();
}, []);
return { geo, status };
}2. 为什么开发环境下请求可能会触发两次
React 的 StrictMode 会在开发环境下有意地将每个组件挂载、卸载并重新挂载一次,以便暴露那些没有正确清理副作用的问题。这意味着该 hook 的 effect 会运行两次,上文的 AbortController 因此十分重要:第一次请求会在其完成之前被清理函数中止,因此实际上只有一次真正的 getGeo() 调用会完成。省略中止调用并不会破坏正确性——GET /v1/geo 是一个只读操作——但会遗留一个多余的在途请求,以及一次被丢弃的首个实例永远用不到的状态更新。
3. 渲染加载、结果与回退状态
像使用任何其他数据 hook 一样使用该 hook,在 status 离开 'loading' 之前保持一个合理的默认值可见。
TypeScript (JSX)
function RegionBanner() {
const { geo, status } = useVisitorGeo();
if (status === "loading") return <p>正在检测您的地区…</p>;
if (status === "error" || !geo?.country) return <p>使用默认地区</p>;
return <p>为 {geo.city ?? geo.country} 显示内容</p>;
}4. 保留访问者的明确选择
与纯 JavaScript 指南中的规则相同:在应用建议的国家之前,请先检查是否存在已保存的偏好设置,只有在尚不存在偏好设置时,才回退使用 hook 的结果。
TypeScript
const preferred = localStorage.getItem("preferredCountry");
const country = preferred ?? geo?.country ?? "US";错误与边界情况
不被允许的来源会在您的代码看到正常响应之前就已失败——请先将应用的开发与生产 URL 添加到该密钥的允许来源中。GeoResponse 上除 ip 与 isEUCountry 之外的每个字段都可能为 null,因此仅判断 status === 'ready' 并不够;还需要像上文回退逻辑中的 geo?.country 那样,检查您即将渲染的具体字段。
下一步
正在使用 Next.js?它的服务器渲染模型会改变该请求可以安全运行的位置——请参阅 Next.js 指南,了解客户端/服务器的划分方式,以及针对提供的 IP 的私密密钥服务器端模式。