mirror of
https://github.com/duckietm/Nitro-V3.git
synced 2026-06-20 07:26:19 +00:00
fix(snapshots): re-apply the 3 snapshot-consumer migrations with the use-between/useSyncExternalStore incompatibility resolved
Root cause of last session's "(intermediate value)() is undefined" at ToolbarView.tsx:46: use-between 1.x ships its own React-dispatcher proxy (ownDispatcher in node_modules/use-between/release/index.esm.js:54-169) that re-implements only useState, useReducer, useEffect, useLayoutEffect, useCallback, useMemo, useRef and useImperativeHandle. It does NOT implement useSyncExternalStore. When the inner state function of useBetween(stateFn) calls useSyncExternalStore (directly or via useExternalSnapshot / useUserDataSnapshot), React resolves the dispatcher to use-between's proxy, finds .useSyncExternalStore missing, and calls undefined() — that's the exact production crash in Firefox. Chrome reports the same as "dispatcher.useSyncExternalStore is not a function". Neither the vite alias (790ad2b) nor the defensive renderer-method guards (c35a2d4) could fix it — both addressed downstream symptoms (stale dist / missing manager methods) but the dispatcher is upstream of both. That's why every retry kept reproducing the same error. Fix is structural: snapshot hooks (useUserDataSnapshot, useIsUserIgnored, etc.) MUST run outside any useBetween scope. Three re-applied migrations: - useSessionInfo: snapshot read moved into the outer wrapper. The inner useSessionInfoState (useBetween-shared) now contains only use-between-safe hooks: useState, useMessageEvent, plain actions. userFigure / userRespectRemaining / petRespectRemaining come from useUserDataSnapshot() OUTSIDE useBetween, so useSyncExternalStore installs against the real React dispatcher. - useChatWidget.ownUserId: direct snapshot read. useChatWidget is exported as `useChatWidget = useChatWidgetState` (NOT wrapped in useBetween), so this hook never sat inside the broken scope — the precautionary rollback was unnecessary in retrospect. Gains session-change reactivity (e.g. reconnect under a different user id). - AvatarInfoWidgetAvatarView Ignore/Unignore: useIsUserIgnored(name) read directly in the component body. Same reasoning as useChatWidget — never inside useBetween. The menu auto-flips Ignore <-> Unignore while the popup is open. Added regression guard at src/hooks/session/useSessionSnapshots.test.tsx with two cases: (1) useSyncExternalStore inside useBetween throws, (2) useSyncExternalStore outside useBetween in the same render works. Pins the constraint so future migrations cannot reintroduce the bad shape silently. Verification: yarn typecheck clean, yarn test 209/209 (207 baseline + 2 new regression cases), no consumer surface changes — every destructured field (userFigure, userRespectRemaining, respectUser, petRespectRemaining, respectPet, chatStyleId, updateChatStyleId) is still returned with the same name and shape.
This commit is contained in:
@@ -2,6 +2,7 @@ import { GetGuestRoomResultEvent, GetRoomEngine, GetSessionDataManager, PetFigur
|
||||
import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
||||
import { ChatBubbleMessage, ChatBubbleUtilities, ChatEntryType, ChatHistoryCurrentDate, GetConfigurationValue, GetRoomObjectScreenLocation, IRoomChatSettings, LocalizeText, PlaySound, RoomChatFormatter } from '../../../api';
|
||||
import { useMessageEvent, useNitroEvent } from '../../events';
|
||||
import { useUserDataSnapshot } from '../../session/useSessionSnapshots';
|
||||
import { useTranslation } from '../../translation';
|
||||
import { useRoom } from '../useRoom';
|
||||
import { useChatHistory } from './../../chat-history';
|
||||
@@ -22,7 +23,12 @@ const useChatWidgetState = () =>
|
||||
const { addChatEntry, updateChatEntry } = useChatHistory();
|
||||
const { settings, translateIncoming, consumeOutgoingTranslation } = useTranslation();
|
||||
const isDisposed = useRef(false);
|
||||
const ownUserId = (GetSessionDataManager()?.userId || -1);
|
||||
// Reactive: re-renders if the session-data snapshot flips (e.g.
|
||||
// reconnect under a different user id). Safe to call here —
|
||||
// useChatWidget is NOT wrapped in useBetween (see export below),
|
||||
// so the real React dispatcher is in scope and
|
||||
// useSyncExternalStore installs correctly.
|
||||
const ownUserId = (useUserDataSnapshot().userId || -1);
|
||||
|
||||
const applyTranslationToBubble = useCallback((chatMessage: ChatBubbleMessage, originalText: string, translatedText: string, detectedLanguage: string, targetLanguage: string) =>
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user