mirror of
https://github.com/mauriceboe/TREK.git
synced 2026-10-18 21:16:47 +00:00
eb8e777741
The last React-18 leftover in the dependency tree. v4 declared
use-sync-external-store as a hard dependency and its main entry imported the
shim; v5 declares no dependencies at all and calls React.useSyncExternalStore
directly. Only zustand/traditional still reaches for the shim, and nothing here
imports it, so it no longer reaches the bundle.
The upgrade itself needed one line. What it needed more of was checking, because
the one thing v5 changes about behaviour does not show up as a test failure.
v4 subscribed through useSyncExternalStoreWithSelector, which memoised the
selector's result. v5 hands React a plain getSnapshot of
`() => selector(api.getState())` and compares with Object.is. A selector that
builds a fresh object or array therefore returns a new reference on every call,
and React reads that as "the store changed" — an infinite render loop, or at
best a re-render on every parent render.
All 294 store selectors return a property or a primitive. None builds an object
literal, array literal, filter/map/slice result, `?? []` default, Object.keys or
a new Set/Map, so none of them needed useShallow. The suite confirms it: 533
files, 11,165 tests, and zero "getSnapshot should be cached" or "Maximum update
depth" warnings anywhere in the output.
The one source change is the seedStore test helper, which was typed against a
hand-written `{ setState: (partial, replace?: boolean) => void }` shape. As of
v5 setState is an overload pair — partial with replace?: false, full state with
replace: true — and no single signature is assignable to that. Typing it against
zustand's own StoreApi fixes all 129 resulting errors and is what the helper
meant in the first place.
48 lines
2.2 KiB
TypeScript
48 lines
2.2 KiB
TypeScript
import type { StoreApi } from 'zustand';
|
|
import { useAuthStore } from '../../src/store/authStore';
|
|
import { useTripStore } from '../../src/store/tripStore';
|
|
import { useSettingsStore } from '../../src/store/settingsStore';
|
|
import { useVacayStore } from '../../src/store/vacayStore';
|
|
import { useAddonStore } from '../../src/store/addonStore';
|
|
import { useInAppNotificationStore } from '../../src/store/inAppNotificationStore';
|
|
import { usePermissionsStore } from '../../src/store/permissionsStore';
|
|
// Journey store is reset individually in journey tests to avoid circular import issues
|
|
|
|
// Capture initial states at import time (before any test modifies them)
|
|
const initialAuthState = useAuthStore.getState();
|
|
const initialTripState = useTripStore.getState();
|
|
const initialSettingsState = useSettingsStore.getState();
|
|
const initialVacayState = useVacayStore.getState();
|
|
const initialAddonState = useAddonStore.getState();
|
|
const initialNotifState = useInAppNotificationStore.getState();
|
|
const initialPermsState = usePermissionsStore.getState();
|
|
export function resetAllStores(): void {
|
|
useAuthStore.setState(initialAuthState, true);
|
|
useTripStore.setState(initialTripState, true);
|
|
useSettingsStore.setState(initialSettingsState, true);
|
|
useVacayStore.setState(initialVacayState, true);
|
|
useAddonStore.setState(initialAddonState, true);
|
|
useInAppNotificationStore.setState(initialNotifState, true);
|
|
usePermissionsStore.setState(initialPermsState, true);
|
|
}
|
|
|
|
/**
|
|
* Tests routinely seed a store with a partially-populated slice of state,
|
|
* including partial nested objects (e.g. only `settings.time_format`). The
|
|
* store's own setState wants the exact field types, so seeding accepts a
|
|
* deep-partial view and casts at the boundary.
|
|
*
|
|
* Typed against zustand's own StoreApi rather than a hand-written
|
|
* `{ setState: ... }` shape: as of v5 setState is an overload pair (partial +
|
|
* replace?: false, full state + replace: true), and no single signature is
|
|
* assignable to it.
|
|
*/
|
|
type DeepPartial<T> = T extends object ? { [P in keyof T]?: DeepPartial<T[P]> } : T;
|
|
|
|
export function seedStore<T extends object>(
|
|
store: Pick<StoreApi<T>, 'setState'>,
|
|
state: DeepPartial<T>,
|
|
): void {
|
|
store.setState(state as Partial<T>);
|
|
}
|