mirror of
https://github.com/duckietm/Nitro-V3.git
synced 2026-06-19 15:06:20 +00:00
docs(CLAUDE.md,ARCHITECTURE.md): record snapshot-consumer rollback (e142efd)
CLAUDE.md updates:
- Patterns to use: "useSessionSnapshots" section retitled "(OPT-IN)";
the documented pilot adopters (useSessionInfo,
AvatarInfoWidgetAvatarView) are removed. Adds explicit warning about
the suspected useBetween + useSyncExternalStore + React Compiler
interaction and the rollback in e142efd.
- Adopted table: snapshot-consumer row changed to "No in-tree
consumers" with note about defensive fallbacks remaining.
- Not yet table: the useChatWidget reactive-ownUserId line corrected
to reflect the rollback; the "migrate session-data mirrors" row
marked BLOCKED with a retry hint (try a non-useBetween consumer
first to isolate the cause).
ARCHITECTURE.md update:
- useExternalSnapshot bullet in the "Solution" section gains a note
pointing at the 8 pre-built consumers in useSessionSnapshots.ts and
the 2026-05-18 rollback caveat with the suspected interaction and
retry guidance.
Pure documentation refresh; no code change. The useSessionSnapshots.ts
file and the vite alias remain in place — they're not what got rolled
back, only the consumer-side migrations were.
This commit is contained in:
@@ -127,12 +127,12 @@ canonical pattern.
|
|||||||
|
|
||||||
## Patterns to use
|
## Patterns to use
|
||||||
|
|
||||||
### `useSessionSnapshots` (renderer snapshot pattern, React-side)
|
### `useSessionSnapshots` (renderer snapshot pattern, React-side — OPT-IN)
|
||||||
|
|
||||||
For state that lives on a renderer Manager and is invalidated through
|
For state that lives on a renderer Manager and is invalidated through
|
||||||
`NitroEventType.*_UPDATED`, prefer the snapshot consumer hooks in
|
`NitroEventType.*_UPDATED`, the file
|
||||||
`src/hooks/session/useSessionSnapshots.ts` over `useState +
|
`src/hooks/session/useSessionSnapshots.ts` exposes eight consumer hooks
|
||||||
useMessageEvent` mirrors:
|
backed by `useSyncExternalStore`:
|
||||||
|
|
||||||
```ts
|
```ts
|
||||||
const userData = useUserDataSnapshot(); // SessionData
|
const userData = useUserDataSnapshot(); // SessionData
|
||||||
@@ -145,12 +145,21 @@ const vols = useVolumesSnapshot(); // sound volumes
|
|||||||
const users = useRoomUserListSnapshot(); // ReadonlyArray<IRoomUserData>
|
const users = useRoomUserListSnapshot(); // ReadonlyArray<IRoomUserData>
|
||||||
```
|
```
|
||||||
|
|
||||||
Each is a thin `useSyncExternalStore` wrapper around the renderer's
|
Each hook has defensive `typeof method === 'function'` guards against
|
||||||
matching `getXxxSnapshot()` + subscription to the matching event.
|
a stale renderer bundle and degrades to a frozen default snapshot if
|
||||||
Snapshot references are renderer-guaranteed stable until invalidation
|
the renderer doesn't expose the matching getter (kept module-level so
|
||||||
— React bails out cleanly when nothing changed. Pilot adopters:
|
React's bailout still works on the degraded path).
|
||||||
`useSessionInfo` (userFigure / respects), `AvatarInfoWidgetAvatarView`
|
|
||||||
(reactive Ignore/Unignore menu entry).
|
**Adoption status: zero in-tree consumers.** The first three pilot
|
||||||
|
migrations (`useSessionInfo`, `useChatWidget.ownUserId`,
|
||||||
|
`AvatarInfoWidgetAvatarView` Ignore/Unignore) were rolled back in
|
||||||
|
`e142efd` after a persistent runtime error
|
||||||
|
`(intermediate value)() is undefined` at `ToolbarView.tsx:46` that the
|
||||||
|
vite-alias fix (`790ad2b`) and the defensive guards (`c35a2d4`) could
|
||||||
|
not eliminate. The hooks remain available for any future opt-in
|
||||||
|
consumer, but **do not migrate `useBetween`-shared consumers to them
|
||||||
|
without isolated testing first** — that combination is the suspected
|
||||||
|
cause of the bug.
|
||||||
|
|
||||||
### `useNitroEventState` / `useMessageEventState`
|
### `useNitroEventState` / `useMessageEventState`
|
||||||
|
|
||||||
@@ -285,7 +294,7 @@ into `configurePreviewServer` so `yarn preview` keeps working.
|
|||||||
|
|
||||||
| Adopted | Pilot sites |
|
| Adopted | Pilot sites |
|
||||||
|---|---|
|
|---|---|
|
||||||
| Renderer snapshot consumer hooks (`useSessionSnapshots`) | `useSessionInfo` (userFigure / respectsLeft / respectsPetLeft via `useUserDataSnapshot`), `AvatarInfoWidgetAvatarView` (reactive Ignore/Unignore via `useIsUserIgnored`). 8 hooks total available; consumers can read userData / activeRoomSession / ignoredUsers / groupBadges / soundVolumes / roomUserList reactively |
|
| Renderer snapshot consumer hooks (`useSessionSnapshots`) | **No in-tree consumers** — three pilot migrations rolled back in `e142efd` due to a runtime `(intermediate value)() is undefined` at `ToolbarView.tsx:46` that survived both the vite-alias fix and defensive guards. The 8 hooks (userData / activeRoomSession / ignoredUsers / groupBadges / soundVolumes / roomUserList / isUserIgnored / groupBadge) remain available as opt-in API with frozen-default fallbacks. |
|
||||||
| `useNitroEventState` + companions (Reducer, ExternalSnapshot) | `OfferView`, `useAvatarInfoWidget` (figure/badges/group reducer), `useInventoryFurni` (pure reducers + fragments useRef) |
|
| `useNitroEventState` + companions (Reducer, ExternalSnapshot) | `OfferView`, `useAvatarInfoWidget` (figure/badges/group reducer), `useInventoryFurni` (pure reducers + fragments useRef) |
|
||||||
| `useNitroQuery` + `useNitroEventInvalidator` | `OfferView`, `CatalogLayoutRoomAdsView`, `ModToolsChatlogView`, `CfhChatlogView`, `useGiftConfiguration`, `useUserGroups`, `useClubOffers(windowId)`, `useSellablePetPalette(breed)`, `useMarketplaceConfiguration`, `useClubGifts` (with invalidator) |
|
| `useNitroQuery` + `useNitroEventInvalidator` | `OfferView`, `CatalogLayoutRoomAdsView`, `ModToolsChatlogView`, `CfhChatlogView`, `useGiftConfiguration`, `useUserGroups`, `useClubOffers(windowId)`, `useSellablePetPalette(breed)`, `useMarketplaceConfiguration`, `useClubGifts` (with invalidator) |
|
||||||
| Zustand | `NavigatorRoomCreatorView` (`useRoomCreatorStore`), `WiredCreatorToolsView` (`useWiredCreatorToolsUiStore` — every panel-lifecycle-relevant flag, snapshot, selection, highlight, inline editor, picker chain hoisted; what's left in the component as `useState` is genuinely transient: keepSelected, globalClock, roomEnteredAt, selectedMonitorErrorType, selectedMonitorLogDetails) |
|
| Zustand | `NavigatorRoomCreatorView` (`useRoomCreatorStore`), `WiredCreatorToolsView` (`useWiredCreatorToolsUiStore` — every panel-lifecycle-relevant flag, snapshot, selection, highlight, inline editor, picker chain hoisted; what's left in the component as `useState` is genuinely transient: keepSelected, globalClock, roomEnteredAt, selectedMonitorErrorType, selectedMonitorLogDetails) |
|
||||||
@@ -298,9 +307,9 @@ into `configurePreviewServer` so `yarn preview` keeps working.
|
|||||||
|
|
||||||
| Not yet | Notes |
|
| Not yet | Notes |
|
||||||
|---|---|
|
|---|---|
|
||||||
| Split `useChatWidget` / `useAvatarInfoWidget` (data/actions) | Both state-driven via events with no clean imperative actions to extract — split still skip-motivated, but each got a targeted tidy in 2026-05-18 (useChatWidget: reactive `ownUserId` via `useUserDataSnapshot`; useAvatarInfoWidget: typed `__nitroAvatarClickControl` accessor + module-scope DEBOUNCE const). Further work would have to be feature-driven. |
|
| Split `useChatWidget` / `useAvatarInfoWidget` (data/actions) | Both state-driven via events with no clean imperative actions to extract — split still skip-motivated, but `useAvatarInfoWidget` got a typed `__nitroAvatarClickControl` accessor + module-scope DEBOUNCE const in 2026-05-18 (commit `05ff7df`). The `useChatWidget` reactive-`ownUserId` migration in the same commit was rolled back in `e142efd`; the hook is back on `GetSessionDataManager()?.userId` (static at mount). |
|
||||||
| Split `usePetPackageWidget` / `useWordQuizWidget` / `useChatCommandSelector` (data/actions) | Data/actions split remains a bad fit, but all three got real modernization in 2026-05-18 instead: usePetPackageWidget → useReducer + extracted `getPetPackageNameError` pure helper + 4 tests; useWordQuizWidget → fixed stale-closure bug in `setUserAnswers` updater + `useRef` for the timeout handle; useChatCommandSelector → module-level `let` cache replaced with a Zustand store. |
|
| Split `usePetPackageWidget` / `useWordQuizWidget` / `useChatCommandSelector` (data/actions) | Data/actions split remains a bad fit, but all three got real modernization in 2026-05-18 instead: usePetPackageWidget → useReducer + extracted `getPetPackageNameError` pure helper + 4 tests; useWordQuizWidget → fixed stale-closure bug in `setUserAnswers` updater + `useRef` for the timeout handle; useChatCommandSelector → module-level `let` cache replaced with a Zustand store. |
|
||||||
| Migrate remaining `useSessionInfo`-style mirrors to renderer snapshots | Pilot done on `useSessionInfo` + `AvatarInfoWidgetAvatarView` + `useChatWidget.ownUserId`. Other candidates: any place that reads `GetSessionDataManager().userId/figure/clubLevel/isModerator` etc. directly in render and never re-renders on session changes. Each is a small migration; no need to bundle. |
|
| Migrate any consumer to renderer snapshot hooks | **Blocked.** Three pilot migrations were rolled back in `e142efd` after the `useSessionInfo` migration triggered a persistent runtime error at `ToolbarView.tsx:46`. The defensive guards in `useSessionSnapshots.ts` and the umbrella vite alias (`790ad2b`) are still in place. Before retrying, isolate the cause: the suspected interaction is `useBetween` + `useSyncExternalStore` + React Compiler. Try a NON-`useBetween` consumer first (e.g. a fresh per-component hook usage in a low-blast-radius widget). |
|
||||||
| Widen the component / hook test coverage | Mock layer is in place (`src/nitro-renderer.mock.ts`) and 3+ hook/component pilots pass. Good follow-up targets: `LoginView` Form Actions happy/error paths, `OfferView` with `useNitroQuery`. (Acceptable only as a side-effect of a real change — coverage growth on its own is deprioritized per session feedback.) |
|
| Widen the component / hook test coverage | Mock layer is in place (`src/nitro-renderer.mock.ts`) and 3+ hook/component pilots pass. Good follow-up targets: `LoginView` Form Actions happy/error paths, `OfferView` with `useNitroQuery`. (Acceptable only as a side-effect of a real change — coverage growth on its own is deprioritized per session feedback.) |
|
||||||
|
|
||||||
## Known open logic bugs
|
## Known open logic bugs
|
||||||
|
|||||||
+15
-1
@@ -103,7 +103,21 @@ information when forced into a single selector.
|
|||||||
`useSyncExternalStore` wrapper pairing the renderer's
|
`useSyncExternalStore` wrapper pairing the renderer's
|
||||||
`EventDispatcher.subscribe()` with the `getXxxSnapshot()` getters
|
`EventDispatcher.subscribe()` with the `getXxxSnapshot()` getters
|
||||||
added in renderer 2.1.0. Use this for readonly views over manager
|
added in renderer 2.1.0. Use this for readonly views over manager
|
||||||
state (`getUserDataSnapshot`, `getActiveRoomSessionSnapshot`).
|
state. Eight pre-built consumers live in
|
||||||
|
`src/hooks/session/useSessionSnapshots.ts` (userData / activeRoomSession
|
||||||
|
/ ignoredUsers / groupBadges / soundVolumes / roomUserList + scalar
|
||||||
|
derivations `useIsUserIgnored`, `useGroupBadge`), each with defensive
|
||||||
|
`typeof` guards against a stale renderer bundle.
|
||||||
|
|
||||||
|
**Note (2026-05-18):** the first three pilot migrations (`useSessionInfo`,
|
||||||
|
`useChatWidget.ownUserId`, `AvatarInfoWidgetAvatarView` Ignore-menu)
|
||||||
|
were rolled back in `e142efd` after a persistent runtime error
|
||||||
|
`(intermediate value)() is undefined` at `ToolbarView.tsx:46` that
|
||||||
|
the vite-alias fix (`790ad2b`) and defensive guards (`c35a2d4`) could
|
||||||
|
not eliminate. Suspected interaction: `useBetween` +
|
||||||
|
`useSyncExternalStore` + React Compiler. Before retrying any
|
||||||
|
migration here, exercise the snapshot hooks from a non-`useBetween`
|
||||||
|
consumer in a low-blast-radius widget first to isolate the cause.
|
||||||
|
|
||||||
For state owned outside the listener (the `useState` + `setState(prev =>
|
For state owned outside the listener (the `useState` + `setState(prev =>
|
||||||
applyX(prev, event))` pattern), keep using `useNitroEvent` /
|
applyX(prev, event))` pattern), keep using `useNitroEvent` /
|
||||||
|
|||||||
Reference in New Issue
Block a user