Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
95 commits
Select commit Hold shift + click to select a range
2144f44
Add alien-signals
markerikson Jun 5, 2026
947ca87
Implement initial alien-signals path registry
markerikson Jun 5, 2026
72683eb
Implement tracking proxy
markerikson Jun 5, 2026
02c2942
Add diff engine
markerikson Jun 5, 2026
4e400d8
Implement initial useSignalSelector and SignalProvider
markerikson Jun 5, 2026
4795703
Use string paths, tracked-only diffs, and shared root proxy
markerikson Jun 5, 2026
0556979
Improve intermediate object handling
markerikson Jun 6, 2026
af0ecc2
Optimize key checks and proxy creation
markerikson Jun 6, 2026
7c52af0
Use parent-child index for path registry
markerikson Jun 7, 2026
504c18a
Add benchmarks for reconciliation logic
markerikson Jun 7, 2026
d7cc9ac
Implement array identity tracking
markerikson Jun 7, 2026
c86c2ca
Improve array append/prune case perf
markerikson Jun 7, 2026
f0e8340
Implement proxy array method overrides
markerikson Jun 7, 2026
f92ea9f
Split object and array paths for v8 optimization
markerikson Jun 7, 2026
bde96b4
Export SignalProvider and useSignalSelector
markerikson Jun 9, 2026
8e04f1b
Fix lint issues
markerikson Jun 9, 2026
a83b7e4
Fix test and import type issues
markerikson Jun 9, 2026
44144ac
Track parents for leaf values
markerikson Jun 10, 2026
252aee6
Unwrap proxy values for array reference comparisons
markerikson Jun 10, 2026
90f7c6a
Add more identity comparison tests
markerikson Jun 10, 2026
7a7ef9a
Handle item swaps while pruning
markerikson Jun 10, 2026
f5b0e79
Add unwrap method for proxy edge cases
markerikson Jun 11, 2026
2f6d974
Add tests for array method cases
markerikson Jun 11, 2026
c573d2a
Always register dependencies on arrays during method calls
markerikson Jun 11, 2026
15e2273
Track column & structural signals for array scan methods
markerikson Jul 26, 2026
9858c6a
Fix effect scope teardown in useSignalSelector (use scope.stop())
markerikson Jul 26, 2026
02a833e
Use coarse array signals for primitive array elements
markerikson Jul 29, 2026
9295474
Reduce per-proxy allocation overhead
markerikson Jul 29, 2026
4c70612
Add tests for known edge cases
markerikson Jul 29, 2026
1c45fe3
Fix multiple edge case bugs
markerikson Jul 29, 2026
d78d6ba
Escape reserved path characters in object keys and column props
markerikson Jul 29, 2026
74af43d
Add recursive untrack at the hook boundary
markerikson Jul 29, 2026
ad7498e
Add untrack boundary benchmark
markerikson Jul 29, 2026
b5fb99d
Add registry (Immer-style) untrack strategy and benchmark both
markerikson Jul 29, 2026
d2410e4
Remove registry untrack strategy; ship recursive only
markerikson Jul 30, 2026
ac30412
Fix zombie child crashes and prune signal misfires
markerikson Jul 30, 2026
3fd2b1d
Add useSelector API parity: options arg, dev checks, withTypes
markerikson Jul 30, 2026
0cc9858
Add createSignalSelectorHook for custom-context parity
markerikson Jul 30, 2026
a608d88
Add render-phase swap tests documenting eval counts and batching cons…
markerikson Jul 30, 2026
21f8325
Fix lint errors and use built-in NoInfer for upstream compat
markerikson Jul 30, 2026
ee006cf
Use inferred spy type instead of MockInstance generics
markerikson Jul 30, 2026
6f932dc
Add coarse handling to improve initial mount
markerikson Jul 31, 2026
f7b49a4
Improve perf with new map/set instances instead of clearing
markerikson Aug 1, 2026
9abe680
Add interop tests
markerikson Aug 8, 2026
9a9f7e5
Use vendored reactiveSystem layer to clean up path registry
markerikson Aug 9, 2026
c4983ba
Combine common ancestor path chains for cleanup
markerikson Aug 9, 2026
02e6f83
Fix a couple bugs and improve useSelector test compat
markerikson Aug 9, 2026
966262f
Allow running standard tests with signal selectors overridden
markerikson Aug 10, 2026
c22b087
Add failing tests for Suspense/transition/StrictMode uncommitted-hook…
markerikson Aug 15, 2026
722af34
Restructure deep tier to avoid subscriptions while rendering
markerikson Aug 15, 2026
b946fb6
Remove engine from the public signals API
markerikson Aug 16, 2026
9a2ec0d
Clean up dead code, stale comments, and duplicated path building
markerikson Aug 16, 2026
0573ce4
Support swapping the store prop on SignalProvider
markerikson Aug 16, 2026
322c938
Strengthen signals test suite: exact assertions and new coverage
markerikson Aug 16, 2026
cc02981
Add Reselect interop tests for the signals implementation
markerikson Aug 16, 2026
8cf23db
Add react-redux/signals entry point
markerikson Aug 16, 2026
16f9878
Add mutable-state interop tests pinning behavior with non-immutable s…
markerikson Aug 27, 2026
4e25b07
Reject selector writes to state consistently in both tiers
markerikson Aug 27, 2026
6e108e1
Fix lint errors: jsdoc tags and leading semicolons
markerikson Aug 27, 2026
f91d074
Modernize signals branch for pnpm, oxlint, oxfmt, and tsdown
markerikson Sep 10, 2026
2e0acec
docs: split useSelector into its own API page
markerikson Aug 16, 2026
08050c7
docs: split useDispatch into its own API page
markerikson Aug 16, 2026
d885e60
docs: split useStore into its own API page
markerikson Aug 16, 2026
7e2576a
docs: rewrite Hooks page as an overview shell
markerikson Aug 16, 2026
89f18fe
docs: add SignalProvider API page
markerikson Aug 16, 2026
64737cf
docs: add useSignalSelector API page
markerikson Aug 16, 2026
81edf0e
docs: add unwrap API page
markerikson Aug 16, 2026
1576484
docs: recategorize API sidebar into Components/Hooks/Other
markerikson Aug 16, 2026
693f543
docs: document that useSignalSelector rejects state mutations
markerikson Sep 10, 2026
057817d
Build each entry point separately so the main bundle stays self-conta…
markerikson Sep 19, 2026
1eea3a0
Make the reactive system tree-shakeable from the main entry point
markerikson Sep 19, 2026
63fb0eb
Add a CI example that checks signals code tree-shakes out of stock apps
markerikson Sep 19, 2026
c6ffabf
Skip node10 resolution in the attw check so subpath entries can be in…
markerikson Sep 19, 2026
76614c5
Add an npm dist-tag input to the publish workflow
markerikson Sep 22, 2026
6dae195
Move tree-shaking example scripts into scripts/ and add a bundle-size…
markerikson Sep 22, 2026
beeec68
Report react-redux's own bundle cost separately in the size measurement
markerikson Sep 22, 2026
52478dc
Release 9.4.0-alpha.0
markerikson Sep 22, 2026
5b3e7f8
Add tests for memoized-selector cache hits and conditional reads
markerikson Sep 24, 2026
fbb2835
Test mixed memoize/argsMemoize combinations and shallowEqual args
markerikson Sep 24, 2026
154319f
Create a fresh root tracking proxy on every evaluation
markerikson Sep 24, 2026
46b5e94
Share top-level path keys across root proxies
markerikson Sep 24, 2026
f698306
Return raw elements with identity dependencies from array methods
markerikson Sep 24, 2026
176ebc3
Add regression test for element-signal churn with memoized selectAll
markerikson Sep 24, 2026
c0bc592
Depend on the container, not each element, for mapped tracking proxies
markerikson Sep 24, 2026
3be70bb
Memoize repeated primitive reads within one evaluation and warn on ho…
markerikson Sep 24, 2026
ab58b94
docs: describe array-method results, Reselect interaction, and repeat…
markerikson Sep 24, 2026
57c53f4
Release 9.4.0-alpha.1
markerikson Sep 24, 2026
1855613
Add tests for RTKQ custom React hooks module interop
markerikson Sep 26, 2026
8c18c18
Fix store update and notification timing issues
markerikson Sep 26, 2026
16bc4a7
Rewrite quadratic unsubscribe test to avoid timing dependency
markerikson Sep 26, 2026
bd84bc6
Force RTK import of R-R to resolve to local source
markerikson Sep 29, 2026
6781a72
Collapse enumeration reads into fewer signals
markerikson Sep 29, 2026
995a4d9
Ensure RTKQ lastResult doesn't end up as a stale proxy
markerikson Sep 29, 2026
3bc4176
Bump RTK dev dep
markerikson Sep 29, 2026
457f0b3
Release 9.4.0-alpha.2
markerikson Sep 29, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 12 additions & 1 deletion .github/workflows/publish.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,17 @@ on:
#release:
# types: [published]
workflow_dispatch:
inputs:
tag:
description: 'NPM dist-tag (latest, alpha, beta, next)'
required: true
default: 'latest'
type: choice
options:
- latest
- next
- alpha
- beta

permissions: {}

Expand All @@ -29,4 +40,4 @@ jobs:
cache: 'pnpm'
- run: pnpm install --frozen-lockfile
- run: pnpm test
- run: npm publish --access public --provenance
- run: npm publish --access public --provenance --tag ${{ inputs.tag }}
8 changes: 6 additions & 2 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -213,8 +213,12 @@ jobs:
run: pnpm install

# Note: We currently expect "FalseCJS" failures for Node16 + `moduleResolution: "node16",
# `--profile node16` drops the node10 resolution check. The subpath
# entry points (`alternate-renderers`, `signals`) only resolve through
# the `exports` map, which node10 does not read, and we do not support
# Node 10 style resolution for them.
- name: Run are-the-types-wrong
run: pnpm exec attw ./package.tgz --format table --ignore-rules false-cjs --exclude-entrypoints alternate-renderers
run: pnpm exec attw ./package.tgz --format table --ignore-rules false-cjs --profile node16

test-published-artifact:
name: Test Published Artifact ${{ matrix.example }}
Expand Down Expand Up @@ -330,7 +334,7 @@ jobs:
fail-fast: false
matrix:
node: ['24.x']
example: ['rr-rsc-context']
example: ['rr-rsc-context', 'rr-tree-shaking']
defaults:
run:
working-directory: ./examples/publish-ci/${{ matrix.example }}
Expand Down
10 changes: 10 additions & 0 deletions .oxlintrc.json
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,16 @@
"rules": {
"no-unused-expressions": "off"
}
},
{
"files": [
"test/signals/diff.spec.ts",
"test/signals/identityArrayTracking.spec.ts",
"test/signals/trackingProxy.spec.ts"
],
"rules": {
"no-unused-expressions": "off"
}
}
]
}
2 changes: 2 additions & 0 deletions docs/api/Provider.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ Since any React component in a React Redux app can be connected to the store, mo

The [Hooks](./hooks.md) and [`connect`](./connect.md) APIs can then access the provided store instance via React's Context mechanism.

If you want to use the [`useSignalSelector`](./useSignalSelector.md) hook, use the drop-in [`<SignalProvider>`](./SignalProvider.md) replacement instead.

### Props

```ts
Expand Down
82 changes: 82 additions & 0 deletions docs/api/SignalProvider.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
---
id: SignalProvider
title: SignalProvider
sidebar_label: SignalProvider
hide_title: true
description: 'API > SignalProvider: a drop-in replacement for Provider that enables signal-based selector tracking'
---

&nbsp;

# `SignalProvider`

_added in v9.4.0_

`<SignalProvider>` is a drop-in replacement for [`<Provider>`](./Provider.md) that enables fine-grained, signal-based state tracking for the [`useSignalSelector`](./useSignalSelector.md) hook.

Like `<Provider>`, it makes the Redux `store` available to any nested components. In addition, it maintains an internal graph of "path signals" that represent individual fields within the Redux state. On every dispatch, it diffs the previous and next state, and updates only the signals for the fields that actually changed. `useSignalSelector` uses that graph to re-run selectors only when a field they actually read has changed.

```jsx
import { SignalProvider } from 'react-redux'

const store = createStore(rootReducer)

const root = ReactDOM.createRoot(document.getElementById('root'))
root.render(
<SignalProvider store={store}>
<App />
</SignalProvider>,
)
```

:::tip

All of the standard React Redux APIs - [`useSelector`](./useSelector.md), [`useDispatch`](./useDispatch.md), [`useStore`](./useStore.md), and [`connect`](./connect.md) - work unchanged inside a `<SignalProvider>`. They receive the plain Redux state and behave exactly as they do under `<Provider>`. Only components that use `useSignalSelector` opt in to the fine-grained tracking behavior.

:::

## API Reference

### Props

`SignalProvider` accepts the same props as [`<Provider>`](./Provider.md):

| Name | Description |
| :----------------------- | :-------------------------------------------------------------------------------------------------------------------------------------------------- |
| `store` | The single Redux store in your application. |
| `children` | The top-level React elements in your component tree. |
| `context?` | An optional custom context instance, as with `<Provider>`. |
| `serverState?` | An optional server state snapshot, used during initial hydration to keep the UI output consistent with the server-generated HTML. |
| `stabilityCheck?` | Global default for the `useSelector`/`useSignalSelector` [selector stability check](./useSelector.md#selector-result-stability) (development only). |
| `identityFunctionCheck?` | Global default for the [identity function check](./useSelector.md#identity-function-state--state-check) (development only). |

## Usage Guide

### Differences from `Provider`

`<SignalProvider>` behaves like `<Provider>`, with these differences:

#### The state must be a plain object

The root of your Redux state must be a plain JavaScript object, because the state diffing and path tracking operate on its keys. This is reflected in the types: `SignalProvider`'s state generic is constrained to `S extends object`, where `Provider` accepts any state type.

Standard Redux usage with `combineReducers` or Redux Toolkit's `configureStore` always produces a plain object root, so this only matters if your root reducer returns a primitive, a class instance, or another non-plain value. Those setups should keep using `<Provider>` and `useSelector`.

#### Every dispatch runs a state diff

Before notifying subscribers, `<SignalProvider>` compares the previous and next state and updates the changed path signals. This is a structural walk of the changed portions of the state, and adds some work to each dispatch in exchange for skipping selector re-runs in components. See the [`useSignalSelector` performance notes](./useSignalSelector.md#performance-characteristics) for the tradeoffs.

#### The signal graph is keyed to the store

Each `<SignalProvider>` owns one signal registry per store instance. Swapping the `store` prop starts over with a fresh registry, the same way `<Provider>` resets its subscriptions. Multiple `<SignalProvider>`s with different stores (via custom contexts) keep fully independent signal graphs.

### When should I use `SignalProvider`?

Use `<SignalProvider>` if you want to adopt [`useSignalSelector`](./useSignalSelector.md) anywhere in your app. Since all the other React Redux APIs work unchanged, it is safe to swap in at the root and migrate components incrementally.

If your app does not use `useSignalSelector`, there is no benefit to `<SignalProvider>` - the per-dispatch diff adds cost without anything consuming its results. Use the standard `<Provider>`.

## See Also

- [`useSignalSelector`](./useSignalSelector.md): the hook that consumes the signal graph
- [`Provider`](./Provider.md): the standard store provider
4 changes: 3 additions & 1 deletion docs/api/batch.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,12 @@ batch((fn: () => void))

_added in v7.0.0_

:::info
:::caution Deprecated

**If you're using React 18, you do not need to use the `batch` API**. React 18 automatically batches _all_ state updates, no matter where they're queued.

`batch` is now a no-op that immediately runs the callback, and **will be removed in the next major version of React Redux**.

:::

React's `unstable_batchedUpdates()` API allows any React updates in an event loop tick to be batched together into a single render pass. React already uses this internally for its own event handler callbacks. This API is actually part of the renderer packages like ReactDOM and React Native, not the React core itself.
Expand Down
Loading
Loading