diff --git a/src/components/strategy-switcher/index.tsx b/src/components/strategy-switcher/index.tsx index 03a7e88..871b8fd 100644 --- a/src/components/strategy-switcher/index.tsx +++ b/src/components/strategy-switcher/index.tsx @@ -1,11 +1,13 @@ "use client"; -import React, { useCallback, useEffect, useState } from "react"; +import React, { useCallback, useEffect, useSyncExternalStore } from "react"; import { useGameStore } from "@/zustand/game-store"; import { + DEFAULT_STRATEGY, getOrderStrategy, listOrderStrategies, setOrderStrategy, + subscribeOrderStrategy, } from "@/utils/order-strategies"; /** @@ -18,8 +20,13 @@ const enabled = process.env.NODE_ENV !== "production"; const StrategySwitcher: React.FC = () => { const redeal = useGameStore((state) => state.redealCurrentLevel); - const [current, setCurrent] = useState(() => - enabled ? getOrderStrategy().name : "" + // Hydration-safe read of the active strategy: SSR + first client render use the + // build-time default; after hydration the client snapshot resolves localStorage. + // Re-renders when setOrderStrategy fires (e.g. cycling). + const current = useSyncExternalStore( + subscribeOrderStrategy, + () => getOrderStrategy().name, + () => DEFAULT_STRATEGY ); const cycle = useCallback( @@ -28,7 +35,6 @@ const StrategySwitcher: React.FC = () => { const idx = names.indexOf(getOrderStrategy().name); const next = names[(idx + dir + names.length) % names.length]; setOrderStrategy(next); - setCurrent(next); redeal(); }, [redeal] diff --git a/src/utils/order-strategies.ts b/src/utils/order-strategies.ts index eec01c4..7332e85 100644 --- a/src/utils/order-strategies.ts +++ b/src/utils/order-strategies.ts @@ -199,6 +199,17 @@ export const listOrderStrategies = (): Array<{ name: string; description: string const DEV = process.env.NODE_ENV !== "production"; let runtimeOverride: string | null = null; +const strategyListeners = new Set<() => void>(); + +/** Subscribe to active-strategy changes. Returns an unsubscribe fn. + * Lets the dev switcher reflect the strategy via React's useSyncExternalStore + * (hydration-safe: server snapshot = DEFAULT, client snapshot = resolved). */ +export const subscribeOrderStrategy = (cb: () => void): (() => void) => { + strategyListeners.add(cb); + return () => { + strategyListeners.delete(cb); + }; +}; /** * Override the active strategy for this session. Pass a name from `STRATEGIES`, @@ -215,6 +226,7 @@ export const setOrderStrategy = (name: string | null): void => { } catch { /* localStorage unavailable (SSR / privacy mode) — in-memory override still applies */ } + strategyListeners.forEach((cb) => cb()); }; /**