refactor(auth): rename AuthStatusCheckSubmitted → AuthInit (init-only)
The previous event name implied "re-check status", but the actual
semantics is just "read storage on app start and sync loginStatus into
the machine". It was being dispatched from three sites:
1. AuthStatusChecker mount useEffect (the legit one-time init)
2. AuthStatusChecker loginStatus-watching useEffect (dead code — the
machine's onDone already writes loginStatus, re-reading storage
just returns the same value as a no-op)
3. Sidebar post-logout effect (also dead code — AuthReset directly
sets loginStatus to initialState's "notLoggedIn", and
userLogoutActor already cleared storage, so the values are
already aligned)
This commit:
- Renames event AuthStatusCheckSubmitted → AuthInit in:
* auth-events.ts (type union)
* auth-machine.ts (handler + transition target: checkingAuthStatus →
initializing, mirroring UserInit / initializing in user-machine)
* auth-status-checker.tsx (single dispatch site)
* root-providers.tsx (one comment)
- Simplifies auth-status-checker.tsx from 74 → ~40 lines:
* Removes useEffect ② (loginStatus-watching re-check)
* Removes prevLoginStatusRef + skipNextChangeRef + useAuthState
(dead loop-guard machinery no longer needed)
- Removes the post-logout re-check dispatch from sidebar-screen.tsx:
* AuthReset alone is sufficient — userLogoutActor cleared storage
and AuthReset writes back initialState, so they match by
construction. No re-verification needed.
Net: -44 lines, no behavior change for the happy paths (startup,
login, logout), one fewer source of false re-checks.
This commit is contained in:
@@ -1,74 +1,31 @@
|
||||
"use client";
|
||||
/**
|
||||
* AuthStatusChecker 启动 + 状态变化检查器
|
||||
* AuthInit 派发器 —— App 启动时读 storage 同步 loginStatus 到状态机
|
||||
*
|
||||
* 触发时机:
|
||||
* 1. App 启动时(mount)派发一次 `AuthStatusCheckSubmitted`
|
||||
* 2. loginStatus 每次变化时(外部触发)也派发一次 —— 重新读 storage 校准
|
||||
* 例:OAuth sync 完成 → loginStatus 变 "facebook" → re-check 一次确认 storage 一致
|
||||
* 邮箱登录成功 → loginStatus 变 "email" → re-check 确认 token 已落盘
|
||||
* 派发时机:仅 mount 时一次(`useEffect` deps = `[dispatch]`,永远不重跑)。
|
||||
*
|
||||
* 死循环防护(关键 —— 不加会无限递归):
|
||||
* - 状态机收 `AuthStatusCheckSubmitted` → 进 `checkingAuthStatus` → 读 storage → onDone 写回 loginStatus
|
||||
* - 如果 useEffect 直接依赖 `loginStatus`,写回本身触发 effect → 再 dispatch → 死循环
|
||||
* - 用 `skipNextChangeRef` 抑制一次:
|
||||
* 每次我们自己 dispatch 前把 flag 置 true;
|
||||
* effect 见到 loginStatus 变化且 flag 为 true 时跳过并清 flag。
|
||||
* 与原 `AuthStatusCheckSubmitted` 的区别:
|
||||
* - 原版本有两个 useEffect:① mount + ② loginStatus 变化 re-check
|
||||
* - re-check 是 dead code —— 状态机 onDone 已经把 loginStatus 写对,
|
||||
* 再读 storage 只会拿到相同值(no-op),徒增死循环防护复杂度
|
||||
* - 新版本只保留 ①,命名 `AuthInit` 与 `UserInit` 对齐(user-machine 的同模式)
|
||||
*
|
||||
* 与 <OAuthSessionSync /> 平级:
|
||||
* - AuthStatusChecker: 启动 + 状态变化时 re-check("自己也能感知")
|
||||
* - OAuthSessionSync: 持续监听 NextAuth session("别人也在写")
|
||||
* - AuthStatusChecker: 启动时一次性 init("自己启动时同步")
|
||||
* - OAuthSessionSync: 持续监听 NextAuth session("别人在写")
|
||||
*/
|
||||
import { useEffect, useRef } from "react";
|
||||
import { useEffect } from "react";
|
||||
|
||||
import type { LoginStatus } from "@/models/auth/login-status";
|
||||
|
||||
import { useAuthDispatch, useAuthState } from "./auth-context";
|
||||
import { useAuthDispatch } from "./auth-context";
|
||||
|
||||
export function AuthStatusChecker() {
|
||||
const dispatch = useAuthDispatch();
|
||||
const loginStatus = useAuthState().loginStatus;
|
||||
|
||||
// 上一次的 loginStatus(首次 mount 留 null —— 用来跳过首次 effect 跑)
|
||||
const prevLoginStatusRef = useRef<LoginStatus | null>(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" });
|
||||
console.log("[auth-init] mount → dispatch AuthInit");
|
||||
dispatch({ type: "AuthInit" });
|
||||
}, [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;
|
||||
}
|
||||
Reference in New Issue
Block a user