返回首页

自定义 Hooks 示例

演示 src/common/hook 下的 4 个 Hook:触底加载、跨域存储、验证码、安全重定向。

useReachBottom 触底加载

向下滚动到底部自动加载下一页(模拟分页)。

条目 1
条目 2
条目 3
条目 4
条目 5
条目 6
条目 7
条目 8
条目 9
条目 10
条目 11
条目 12
条目 13
条目 14
条目 15
条目 16
条目 17
条目 18
条目 19
条目 20

共 20 条 · lastId = 20

useCrosStorage 跨域存储

跨域/同域下统一读写;未配置 STORAGE_PROXY 时回退到 localStorage/sessionStorage。

结果:—

useCaptcha 验证码

点击图片刷新验证码;需配置 NEXT_PUBLIC_CAPTCHA_URL 才能加载图片。

点击图片刷新验证码

useNavigating 安全重定向

基于 useLocale 拼出带正确语言前缀的重定向地址。

自定义 Hooks(src/common/hook)使用说明

概述

src/common/hook/ 目录封装了 4 个通用 React Hook,覆盖分页加载、跨域存储、验证码、重定向四类场景:

Hook文件用途
useReachBottomReachBottomHook.tsx滚动触底自动加载下一页(无限滚动 / 分页)
useCrosStorageCrosStorageHook.tsx跨域(iframe postMessage)/ 同域统一读写存储
useCaptchaCaptchaHook.tsx验证码图片加载与点击刷新
useNavigatingNavigatingHook.tsx带 locale 前缀的安全重定向

四个 Hook 职责单一,且都对 SSR(typeof window === "undefined")做了防御。


useReachBottom —— 触底加载

监听滚动容器的 scroll 事件,滚动到距底部 20px 内时调用 reachBottomHandler(lastId) 拉取下一页,并用 globalLoading(ref)防抖避免重复请求。

import useReachBottom from "@/common/hook/ReachBottomHook";

const { loading, lastId, containerRef } = useReachBottom(
  async (lastId) => {
    const page = await fetchNextPage(lastId); // 返回 Promise
    return page.newLastId;                     // resolve 出新的游标
  },
  0 // initialLastId
);

return <div ref={containerRef} className="h-96 overflow-y-auto">{/* 列表 */}</div>;

参数

参数类型说明
reachBottomHandler(lastId: any) => Promise<any>触底回调,resolve 出新的 lastId
initialLastIdany初始游标

返回值

字段类型说明
loadingboolean是否加载中
lastIdany当前游标
setLastId(id) => void手动更新游标
startLoading() => void手动标记加载中
finishLoading(newLastId) => void结束加载并更新游标
containerRefRefObject<HTMLDivElement>挂到滚动容器上

注意

  • 触发阈值 20px;请求完成后 globalLoading 延迟 100ms 复位,避免滚动事件短时间重复触发。
  • 内部 useEffect 依赖 [loading]:每次请求完成、loading 翻转时重新绑定 scroll 监听,从而拿到最新的 lastId,避免闭包缓存旧值。

useCrosStorage —— 跨域存储

跨域场景下通过隐藏 iframe + postMessage 与代理页通信;同域(或未配置 STORAGE_PROXY)时直接回退到 localStorage / sessionStorage。常用于多子域共享登录 token。

import useCrosStorage from "@/common/hook/CrosStorageHook";

const crosStorage = useCrosStorage(); // CrosStorage | undefined(首帧为 undefined)

await crosStorage?.set("token-value", "my_token");   // 写入
const v = await crosStorage?.get("my_token");        // 读取
await crosStorage?.remove("my_token");               // 删除

返回值

返回 CrosStorage | undefined,实例提供以下方法:

方法说明
set(value, key?, storage?)写入;key 默认 TOKEN_KEY,storage 默认 AUTOMATIC
get(key?, storage?)读取,resolve 出字符串或 null
remove(key?, storage?)删除,resolve 出被删除的值
getToken / setToken / removeToken针对 token 的快捷方法
locateToken(token?)定位 / 本地化当前登录用户
destroy()移除 iframe(Hook 卸载时会自动调用)

storage 取值:LOCAL / SESSION / AUTOMATIC;AUTOMATIC 由环境变量 NEXT_PUBLIC_TOKEN_STORAGE 决定。

注意:首帧返回 undefined(useEffect 里才 setCrosStorage),使用前需判空。


useCaptcha —— 验证码

返回一个 ref,把它挂到 <img> 上即可:挂载后自动加载验证码,点击图片刷新。加载成功后会把图片 visibility 设为 visible。

import useCaptcha from "@/common/hook/CaptchaHook";

const captchaRef = useCaptcha();

return <img ref={captchaRef} alt="captcha" />;

返回值:RefObject<HTMLImageElement>。

注意:依赖环境变量 NEXT_PUBLIC_CAPTCHA_URL;未配置时图片无法加载。


useNavigating —— 安全重定向

基于 useLocale() 拼出带正确语言前缀的跳转地址,避免手写 URL 导致 locale 丢失。

import useNavigating from "@/common/hook/NavigatingHook";

const Navigate = useNavigating();
// 或按需解构静态方法
const { redirectToLogin } = useNavigating();

Navigate.redirectToIndex();               // 2s 后跳转首页
Navigate.redirectTo("/some/path");        // 立即跳转指定地址
Navigate.redirectToLogin(true, 2000);     // 2s 后跳转登录(可带 ref)

静态方法

方法说明
redirectToIndex(timeout=2000)延迟跳转到 WWW_ROOT/{locale}/
redirectTo(directUrl)立即跳转到指定 URL;为空则走 redirectToIndex
redirectToLogin(withRef=true, timeout=2000)延迟跳转到 PASSPORT_ROOT/{locale}{LOGIN_URL};withRef 为 true 时带上当前 URL 作为 ref

依赖环境变量:NEXT_PUBLIC_WWW_ROOT、NEXT_PUBLIC_PASSPORT_ROOT、NEXT_PUBLIC_LOGIN_URL。