fix(header): publish page title in useLayoutEffect to avoid stale frame on lang switch
Some checks failed
Deploy to Frontend Servers / deploy (push) Failing after 39s

Selecting Bahasa Melayu (or Vietnamese, or any locale whose nav width
crosses the inline/burger threshold) showed a one-frame stutter:

  t=0   cn render: inline nav, title "全部资料"
  t=63  ms render: burger nav, **title still "全部资料"** (stale)
  t=78  ms render: burger nav, title "Semua aset"

The stale title frame came from useSetPageTitle running its setTitle in
a useEffect — that fires after the commit, so the first render after a
lang change still saw the previous page's published title. Combined
with the inline -> burger swap, the two-step update read as the flicker
the user reported.

Switching to useLayoutEffect runs setTitle synchronously between commit
and paint, so the header renders the new title in the same frame as the
new locale. Measured a single cn -> ms transition in the browser: the
intermediate stale-title frame is gone, opacity stays at 1 throughout.
This commit is contained in:
TerryM
2026-06-08 01:18:26 +08:00
parent b5a699c6c7
commit 03a5701798

View File

@@ -1,7 +1,7 @@
import {
createContext,
useContext,
useEffect,
useLayoutEffect,
useState,
type PropsWithChildren,
} from "react";
@@ -31,10 +31,16 @@ export function usePageTitle(): string | null {
return useContext(PageTitleContext)?.title ?? null;
}
/** Publish the current page's title; clears it again when the page unmounts. */
/**
* Publish the current page's title; clears it again when the page unmounts.
* Uses useLayoutEffect so the title updates synchronously with the page render
* — otherwise switching language (e.g. cn -> ms) showed the previous title for
* one paint while the post-commit useEffect was still pending, which read as a
* flicker in the header.
*/
export function useSetPageTitle(title: string | null): void {
const setTitle = useContext(PageTitleContext)?.setTitle;
useEffect(() => {
useLayoutEffect(() => {
setTitle?.(title);
return () => setTitle?.(null);
}, [setTitle, title]);