yuustate 3.9.0 → 3.10.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. package/esm/react.mjs +56 -30
  2. package/package.json +3 -3
  3. package/react.js +55 -29
package/esm/react.mjs CHANGED
@@ -1,4 +1,4 @@
1
- import { useLayoutEffect, useEffect, useState, useRef, useMemo, useCallback } from 'react';
1
+ import { useLayoutEffect, useEffect, useRef, useSyncExternalStore, useCallback, useMemo, useState } from 'react';
2
2
  import { isClient, initStore, getHash, noop } from 'yuustate/vanilla';
3
3
 
4
4
  const useIsomorphicLayoutEffect = isClient ? useLayoutEffect : useEffect;
@@ -64,19 +64,32 @@ const useStoreStateWithInitializer = (store, initialState = NO_INITIAL_VALUE) =>
64
64
  const useStoreState = (store, options = {}) => {
65
65
  const [state] = useStoreStateWithInitializer(store, options.initialState);
66
66
  const [trackedState, usedPathsRef] = useStoreStateProxy(state);
67
- const [, reRender] = useState({});
68
- useEffect(() => {
69
- return store.subscribe((nextState, prevState, changedKeys) => {
70
- const paths = compressPaths(usedPathsRef.current);
71
- for (const path of paths) {
72
- const rootKey = path[0];
73
- if (!changedKeys.includes(rootKey)) continue;
74
- const prevVal = getValueByPath(prevState, path);
75
- const nextVal = getValueByPath(nextState, path);
76
- if (!Object.is(prevVal, nextVal)) return reRender({});
77
- }
78
- });
79
- }, [store]);
67
+ const snapshotRef = useRef(store.getState());
68
+ const getSnapshot = () => snapshotRef.current;
69
+ useSyncExternalStore(
70
+ useCallback(
71
+ (onStoreChange) => {
72
+ return store.subscribe((nextState, prevState, changedKeys) => {
73
+ const paths = compressPaths(usedPathsRef.current);
74
+ for (const path of paths) {
75
+ const rootKey = path[0];
76
+ if (!changedKeys.includes(rootKey)) continue;
77
+ const prevVal = getValueByPath(prevState, path);
78
+ const nextVal = getValueByPath(nextState, path);
79
+ if (!Object.is(prevVal, nextVal)) {
80
+ snapshotRef.current = nextState;
81
+ return onStoreChange();
82
+ }
83
+ }
84
+ });
85
+ },
86
+ // False positive (missing dependency: 'usedPathsRef'): usedPathsRef is a ref, no need to add to deps
87
+ // eslint-disable-next-line react-hooks/exhaustive-deps
88
+ [store]
89
+ ),
90
+ getSnapshot,
91
+ getSnapshot
92
+ );
80
93
  return trackedState;
81
94
  };
82
95
 
@@ -444,22 +457,35 @@ const createQuery = (queryFn, options = {}) => {
444
457
  { ...storeStateToBeUsed, isPending: true }
445
458
  ) : storeStateToBeUsed
446
459
  );
447
- const [, reRender] = useState({});
448
- useEffect(() => {
449
- return store.subscribe((nextState, prevState2, changedKeys) => {
450
- if (prevState2.state === "INITIAL" && !prevState2.isPending && nextState.isPending && !nextState.isRetrying) {
451
- return;
452
- }
453
- const paths = compressPaths(usedPathsRef.current);
454
- for (const path of paths) {
455
- const rootKey = path[0];
456
- if (!changedKeys.includes(rootKey)) continue;
457
- const prevVal = getValueByPath(prevState2, path);
458
- const nextVal = getValueByPath(nextState, path);
459
- if (!Object.is(prevVal, nextVal)) return reRender({});
460
- }
461
- });
462
- }, [store]);
460
+ const snapshotRef = useRef(store.getState());
461
+ const getSnapshot = () => snapshotRef.current;
462
+ useSyncExternalStore(
463
+ useCallback(
464
+ (onStoreChange) => {
465
+ return store.subscribe((nextState, prevState2, changedKeys) => {
466
+ if (prevState2.state === "INITIAL" && !prevState2.isPending && nextState.isPending && !nextState.isRetrying) {
467
+ return;
468
+ }
469
+ const paths = compressPaths(usedPathsRef.current);
470
+ for (const path of paths) {
471
+ const rootKey = path[0];
472
+ if (!changedKeys.includes(rootKey)) continue;
473
+ const prevVal = getValueByPath(prevState2, path);
474
+ const nextVal = getValueByPath(nextState, path);
475
+ if (!Object.is(prevVal, nextVal)) {
476
+ snapshotRef.current = nextState;
477
+ return onStoreChange();
478
+ }
479
+ }
480
+ });
481
+ },
482
+ // False positive (missing dependency: 'usedPathsRef'): usedPathsRef is a ref, no need to add to deps
483
+ // eslint-disable-next-line react-hooks/exhaustive-deps
484
+ [store]
485
+ ),
486
+ getSnapshot,
487
+ getSnapshot
488
+ );
463
489
  useEffect(() => {
464
490
  if (revalidateOnMount !== false) {
465
491
  if (!initialDataIsStale) {
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "yuustate",
3
3
  "description": "Lightweight unified state management for sync and async data.",
4
4
  "private": false,
5
- "version": "3.9.0",
5
+ "version": "3.10.0",
6
6
  "keywords": [
7
7
  "utilities",
8
8
  "store",
@@ -70,8 +70,8 @@
70
70
  },
71
71
  "packageManager": "pnpm@10.32.1",
72
72
  "peerDependencies": {
73
- "@types/react": ">=17.0",
74
- "react": ">=17.0"
73
+ "@types/react": ">=18.0",
74
+ "react": ">=18.0"
75
75
  },
76
76
  "peerDependenciesMeta": {
77
77
  "@types/react": {
package/react.js CHANGED
@@ -66,19 +66,32 @@ const useStoreStateWithInitializer = (store, initialState = NO_INITIAL_VALUE) =>
66
66
  const useStoreState = (store, options = {}) => {
67
67
  const [state] = useStoreStateWithInitializer(store, options.initialState);
68
68
  const [trackedState, usedPathsRef] = useStoreStateProxy(state);
69
- const [, reRender] = react.useState({});
70
- react.useEffect(() => {
71
- return store.subscribe((nextState, prevState, changedKeys) => {
72
- const paths = compressPaths(usedPathsRef.current);
73
- for (const path of paths) {
74
- const rootKey = path[0];
75
- if (!changedKeys.includes(rootKey)) continue;
76
- const prevVal = getValueByPath(prevState, path);
77
- const nextVal = getValueByPath(nextState, path);
78
- if (!Object.is(prevVal, nextVal)) return reRender({});
79
- }
80
- });
81
- }, [store]);
69
+ const snapshotRef = react.useRef(store.getState());
70
+ const getSnapshot = () => snapshotRef.current;
71
+ react.useSyncExternalStore(
72
+ react.useCallback(
73
+ (onStoreChange) => {
74
+ return store.subscribe((nextState, prevState, changedKeys) => {
75
+ const paths = compressPaths(usedPathsRef.current);
76
+ for (const path of paths) {
77
+ const rootKey = path[0];
78
+ if (!changedKeys.includes(rootKey)) continue;
79
+ const prevVal = getValueByPath(prevState, path);
80
+ const nextVal = getValueByPath(nextState, path);
81
+ if (!Object.is(prevVal, nextVal)) {
82
+ snapshotRef.current = nextState;
83
+ return onStoreChange();
84
+ }
85
+ }
86
+ });
87
+ },
88
+ // False positive (missing dependency: 'usedPathsRef'): usedPathsRef is a ref, no need to add to deps
89
+ // eslint-disable-next-line react-hooks/exhaustive-deps
90
+ [store]
91
+ ),
92
+ getSnapshot,
93
+ getSnapshot
94
+ );
82
95
  return trackedState;
83
96
  };
84
97
 
@@ -446,22 +459,35 @@ const createQuery = (queryFn, options = {}) => {
446
459
  { ...storeStateToBeUsed, isPending: true }
447
460
  ) : storeStateToBeUsed
448
461
  );
449
- const [, reRender] = react.useState({});
450
- react.useEffect(() => {
451
- return store.subscribe((nextState, prevState2, changedKeys) => {
452
- if (prevState2.state === "INITIAL" && !prevState2.isPending && nextState.isPending && !nextState.isRetrying) {
453
- return;
454
- }
455
- const paths = compressPaths(usedPathsRef.current);
456
- for (const path of paths) {
457
- const rootKey = path[0];
458
- if (!changedKeys.includes(rootKey)) continue;
459
- const prevVal = getValueByPath(prevState2, path);
460
- const nextVal = getValueByPath(nextState, path);
461
- if (!Object.is(prevVal, nextVal)) return reRender({});
462
- }
463
- });
464
- }, [store]);
462
+ const snapshotRef = react.useRef(store.getState());
463
+ const getSnapshot = () => snapshotRef.current;
464
+ react.useSyncExternalStore(
465
+ react.useCallback(
466
+ (onStoreChange) => {
467
+ return store.subscribe((nextState, prevState2, changedKeys) => {
468
+ if (prevState2.state === "INITIAL" && !prevState2.isPending && nextState.isPending && !nextState.isRetrying) {
469
+ return;
470
+ }
471
+ const paths = compressPaths(usedPathsRef.current);
472
+ for (const path of paths) {
473
+ const rootKey = path[0];
474
+ if (!changedKeys.includes(rootKey)) continue;
475
+ const prevVal = getValueByPath(prevState2, path);
476
+ const nextVal = getValueByPath(nextState, path);
477
+ if (!Object.is(prevVal, nextVal)) {
478
+ snapshotRef.current = nextState;
479
+ return onStoreChange();
480
+ }
481
+ }
482
+ });
483
+ },
484
+ // False positive (missing dependency: 'usedPathsRef'): usedPathsRef is a ref, no need to add to deps
485
+ // eslint-disable-next-line react-hooks/exhaustive-deps
486
+ [store]
487
+ ),
488
+ getSnapshot,
489
+ getSnapshot
490
+ );
465
491
  react.useEffect(() => {
466
492
  if (revalidateOnMount !== false) {
467
493
  if (!initialDataIsStale) {