mirror of
https://github.com/tinyhumansai/openhuman.git
synced 2026-07-27 21:08:00 +00:00
fix(core-state): guard refresh commits after unmount (#1992)
Co-authored-by: Codex <codex@openai.com>
This commit is contained in:
@@ -190,7 +190,12 @@ export default function CoreStateProvider({ children }: { children: ReactNode })
|
||||
const logoutGuardUntilRef = useRef(0);
|
||||
const bootstrapFailCountRef = useRef(0);
|
||||
const refreshInFlightRef = useRef<Promise<void> | null>(null);
|
||||
const isMountedRef = useRef(true);
|
||||
const commitState = useCallback((updater: (previous: CoreState) => CoreState) => {
|
||||
if (!isMountedRef.current) {
|
||||
return;
|
||||
}
|
||||
|
||||
setState(previous => {
|
||||
const next = updater(previous);
|
||||
setCoreStateSnapshot(next);
|
||||
@@ -198,9 +203,21 @@ export default function CoreStateProvider({ children }: { children: ReactNode })
|
||||
});
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
isMountedRef.current = true;
|
||||
return () => {
|
||||
isMountedRef.current = false;
|
||||
snapshotRequestIdRef.current += 1;
|
||||
teamsRequestIdRef.current += 1;
|
||||
};
|
||||
}, []);
|
||||
|
||||
const refreshCore = useCallback(async () => {
|
||||
const requestId = ++snapshotRequestIdRef.current;
|
||||
const snapshot = normalizeSnapshot(await fetchCoreAppSnapshot());
|
||||
if (!isMountedRef.current) {
|
||||
return;
|
||||
}
|
||||
if (!snapshot.sessionToken) {
|
||||
logoutGuardUntilRef.current = 0;
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@ import { beforeEach, describe, expect, it, vi } from 'vitest';
|
||||
|
||||
import * as coreStateApi from '../../services/coreStateApi';
|
||||
import * as tauriCommands from '../../utils/tauriCommands';
|
||||
import { setCoreStateSnapshot } from '../../lib/coreState/store';
|
||||
import { getCoreStateSnapshot, setCoreStateSnapshot } from '../../lib/coreState/store';
|
||||
import { setActiveUserId } from '../../store/userScopedStorage';
|
||||
import CoreStateProvider, {
|
||||
coreStatePollFailureWarningMessage,
|
||||
@@ -221,6 +221,32 @@ describe('CoreStateProvider — identity-change cache clearing', () => {
|
||||
await waitFor(() => expect(screen.getByTestId('ready').textContent).toBe('ready'));
|
||||
});
|
||||
|
||||
it('does not commit a pending bootstrap refresh after unmount', async () => {
|
||||
let resolveSnapshot!: (snapshot: Snapshot) => void;
|
||||
const pendingSnapshot = new Promise<Snapshot>(resolve => {
|
||||
resolveSnapshot = resolve;
|
||||
});
|
||||
fetchSnapshot.mockReturnValue(pendingSnapshot);
|
||||
listTeams.mockResolvedValue([]);
|
||||
|
||||
const { unmount } = render(
|
||||
<CoreStateProvider>
|
||||
<Consumer />
|
||||
</CoreStateProvider>
|
||||
);
|
||||
|
||||
unmount();
|
||||
|
||||
await act(async () => {
|
||||
resolveSnapshot(makeSnapshot({ userId: null, sessionToken: null }));
|
||||
await pendingSnapshot;
|
||||
await Promise.resolve();
|
||||
});
|
||||
|
||||
expect(getCoreStateSnapshot().isReady).toBe(false);
|
||||
expect(getCoreStateSnapshot().snapshot.auth.userId).toBeNull();
|
||||
});
|
||||
|
||||
it('warns when the initial core state poll fails', async () => {
|
||||
const warnSpy = vi.spyOn(console, 'warn').mockImplementation(() => {});
|
||||
|
||||
|
||||
Reference in New Issue
Block a user