Summary
Modernize the persistence rehydration flow to leverage React 19's use() hook for Suspense integration, and wrap rehydration state replacement in startTransition for non-blocking hydration.
Parent issue: #1004
Background
useStoreRehydrated (hooks.js:81-90) currently uses the useState + useEffect pattern:
function useStoreRehydrated() {
const [rehydrated, setRehydrated] = useState(false);
useEffect(() => {
store.persist.resolveRehydration().then(() => setRehydrated(true));
}, []);
return rehydrated;
}
This requires manual if (!rehydrated) checks in consumer code. React 19's use() hook enables Suspense integration instead.
Changes
1. Rewrite useStoreRehydrated using use()
- Replace
useState + useEffect with use(store.persist.resolveRehydration())
- Enables
<Suspense fallback={<Loading/>}> around rehydrating stores
- The current
resolveRehydration() already returns a stable Promise (create-store.js:169)
2. Wrap rehydration replaceState in startTransition
create-store.js:53 — the replaceState call during rehydration is a large state swap
- Wrapping in
startTransition prevents it from blocking the UI
React version strategy
This requires a decision on React 19 support approach:
- Option A: Bump minimum peer dependency to React 19 (breaking change)
- Option B: Conditional export (
easy-peasy/react19) with the use()-based hook
- Option C: Runtime feature detection (
typeof React.use === 'function')
The startTransition portion is React 18 compatible and can land independently.
Testing
- Test Suspense boundary integration with
useStoreRehydrated
- Test with async storage backends (the main rehydration path)
- Test
startTransition wrapping doesn't break rehydration ordering
- Regression test existing
useStoreRehydrated behavior
Summary
Modernize the persistence rehydration flow to leverage React 19's
use()hook for Suspense integration, and wrap rehydration state replacement instartTransitionfor non-blocking hydration.Parent issue: #1004
Background
useStoreRehydrated(hooks.js:81-90) currently uses theuseState+useEffectpattern:This requires manual
if (!rehydrated)checks in consumer code. React 19'suse()hook enables Suspense integration instead.Changes
1. Rewrite
useStoreRehydratedusinguse()useState+useEffectwithuse(store.persist.resolveRehydration())<Suspense fallback={<Loading/>}>around rehydrating storesresolveRehydration()already returns a stable Promise (create-store.js:169)2. Wrap rehydration
replaceStateinstartTransitioncreate-store.js:53— thereplaceStatecall during rehydration is a large state swapstartTransitionprevents it from blocking the UIReact version strategy
This requires a decision on React 19 support approach:
easy-peasy/react19) with theuse()-based hooktypeof React.use === 'function')The
startTransitionportion is React 18 compatible and can land independently.Testing
useStoreRehydratedstartTransitionwrapping doesn't break rehydration orderinguseStoreRehydratedbehavior