"use client"; /** * AuthStatusChecker 启动 + 状态变化检查器 * * 触发时机: * 1. App 启动时(mount)派发一次 `AuthStatusCheckSubmitted` * 2. loginStatus 每次变化时(外部触发)也派发一次 —— 重新读 storage 校准 * 例:OAuth sync 完成 → loginStatus 变 "facebook" → re-check 一次确认 storage 一致 * 邮箱登录成功 → loginStatus 变 "email" → re-check 确认 token 已落盘 * * 死循环防护(关键 —— 不加会无限递归): * - 状态机收 `AuthStatusCheckSubmitted` → 进 `checkingAuthStatus` → 读 storage → onDone 写回 loginStatus * - 如果 useEffect 直接依赖 `loginStatus`,写回本身触发 effect → 再 dispatch → 死循环 * - 用 `skipNextChangeRef` 抑制一次: * 每次我们自己 dispatch 前把 flag 置 true; * effect 见到 loginStatus 变化且 flag 为 true 时跳过并清 flag。 * * 与 平级: * - AuthStatusChecker: 启动 + 状态变化时 re-check("自己也能感知") * - OAuthSessionSync: 持续监听 NextAuth session("别人也在写") */ import { useEffect, useRef } from "react"; import type { LoginStatus } from "@/models/auth/login-status"; import { useAuthDispatch, useAuthState } from "./auth-context"; export function AuthStatusChecker() { const dispatch = useAuthDispatch(); const loginStatus = useAuthState().loginStatus; // 上一次的 loginStatus(首次 mount 留 null —— 用来跳过首次 effect 跑) const prevLoginStatusRef = useRef(null); // 抑制下一次 loginStatus 变化触发的 effect(自己 dispatch 引起的写回要跳过) const skipNextChangeRef = useRef(false); // ───────────────────────────────────────────────────────────── // ① App 启动时:派发一次 // ───────────────────────────────────────────────────────────── useEffect(() => { console.log("[auth-status-checker] mount → dispatch AuthStatusCheckSubmitted"); skipNextChangeRef.current = true; // 抑制启动 check 写回 loginStatus 引起的 effect dispatch({ type: "AuthStatusCheckSubmitted" }); }, [dispatch]); // ───────────────────────────────────────────────────────────── // ② loginStatus 变化时:派发一次(但跳过我们自己 check 写回的那次) // ───────────────────────────────────────────────────────────── useEffect(() => { const prev = prevLoginStatusRef.current; prevLoginStatusRef.current = loginStatus; if (prev === null) return; // 首次 mount —— 启动那次由 ① 处理 if (prev === loginStatus) return; // loginStatus 没变 —— 不触发 if (skipNextChangeRef.current) { // 这是我们自己 check 写回的 —— 抑制本次 skipNextChangeRef.current = false; console.log( "[auth-status-checker] loginStatus change suppressed (self-triggered)", { from: prev, to: loginStatus }, ); return; } // 外部触发(OAuth sync / 邮箱登录 / 登出 等)的 loginStatus 变化 → 再 check 一次校准 skipNextChangeRef.current = true; console.log( "[auth-status-checker] loginStatus changed externally → re-check", { from: prev, to: loginStatus }, ); dispatch({ type: "AuthStatusCheckSubmitted" }); }, [loginStatus, dispatch]); // 无 UI return null; }