Skip to content

refactor(hooks): modernize useSyncExternalStore usage and fix useLocalStore tearing #1013

@ctrlplusb

Description

@ctrlplusb

Summary

Drop the use-sync-external-store polyfill shim and fix a concurrent mode tearing bug in useLocalStore.

Parent issue: #1004

Background

The use-sync-external-store/shim/with-selector polyfill was added for React 16/17 backward compatibility, but peerDependencies already requires ^18.0 || ^19.0. The shim is dead weight. Additionally, useLocalStore still uses the pre-React 18 useState + subscribe pattern which can cause tearing in concurrent mode.

Changes

1. Drop the shim, use native API

  • Remove use-sync-external-store dependency
  • Import useSyncExternalStore natively from react
  • Either use use-sync-external-store/with-selector (non-shim path) or implement selector support manually

2. Remove initializeUseStoreState indirection

  • src/hooks.js:4-9 — the dynamic injection pattern was needed for React 16/17; no longer necessary
  • Import directly in hooks.js

3. Fix useLocalStore concurrent mode tearing

  • src/use-local-store.js:29-36 currently uses useState + useEffect + store.subscribe — the pre-React 18 pattern
  • Migrate to useSyncExternalStore for consistency with useStoreState

Testing

  • All existing tests should continue to pass
  • Add a concurrent mode test for useLocalStore verifying no tearing during rapid updates
  • Verify bundle size reduction from dropping the shim

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions