|
9 | 9 |
|
10 | 10 | > Opinionated optimistic state management for Redux. Tracks transitions alongside reducer state and derives the optimistic view at the selector level — like `git rebase`. No state copies. No checkpoints. |
11 | 11 |
|
12 | | -## Why |
| 12 | +## When to use Optimistron |
13 | 13 |
|
14 | | -Existing optimistic Redux libraries store full state checkpoints to enable rollback: |
| 14 | +Optimistron is a good fit when your app has: |
15 | 15 |
|
16 | | -- **redux-optimist**: saves a complete state snapshot at transaction open, replays all subsequent actions from it on revert |
17 | | -- **redux-optimistic-ui**: wraps state in `{ beforeState, current, history }` — `beforeState` is a full copy of the state tree before any optimistic transaction began |
18 | | -- **Hand-rolled thunk patterns**: `const previous = getState().slice` in a closure, restore on error |
| 16 | +- **Offline-first flows** — users act while disconnected, transitions queue up, conflicts resolve on reconnect. |
| 17 | +- **Async dispatch patterns** — thunks, sagas, listener middleware — anything where you dispatch an intent and later resolve it with success or failure. |
| 18 | +- **Large or normalized state** — where snapshotting the full state tree per in-flight operation gets expensive fast. |
19 | 19 |
|
20 | | -This means memory scales with **state size x number of in-flight operations**. For large normalized stores, each pending action carries a shadow copy. Reverts replay the entire reducer chain — O(actions x reducer cost). |
| 20 | +Other libraries solve optimistic updates in their own way — snapshot/replay, cache patching, query-level invalidation. Optimistron is a different tradeoff: **no state copies, no checkpoints**. Optimistic state is derived at the selector level — which is already memoized by `reselect` in most Redux apps. You get optimistic UI on the read path, with zero write-path overhead. |
21 | 21 |
|
22 | | -Optimistron takes a different approach: **no state copies, no checkpoints**. Optimistic state is derived at the selector level — which is already memoized by `reselect` in most Redux apps. You get optimistic UI for free on the read path, with zero write-path overhead. |
| 22 | +> If you're already using RTK Query's built-in optimistic updates and they cover your needs, you probably don't need this. |
23 | 23 |
|
24 | 24 | --- |
25 | 25 |
|
26 | 26 | ## The Mental Model |
27 | 27 |
|
28 | | -Think of your Redux store like a git repository: |
| 28 | +Think of each reducer you wrap with `optimistron()` as a **branch** — not the whole store. |
29 | 29 |
|
30 | | -- **Committed state** = `main`. Source of truth — only `COMMIT` writes here. |
31 | | -- **Transitions** = staged commits on top. Intended changes that haven't landed yet. |
32 | | -- **`selectOptimistic`** = `rebase`. Replays transitions on committed state at read-time. Never stored — always derived. |
| 30 | +- **Committed state** = the branch tip. Source of truth — only `COMMIT` advances it. |
| 31 | +- **Transitions** = staged commits on top of that branch. Intended changes that haven't landed yet. |
| 32 | +- **`selectOptimistic`** = `rebase`. Replays transitions onto the branch tip at read-time. Never stored — always derived. |
33 | 33 | - **Sanitization** = conflict detection. After every mutation, transitions are replayed. No-ops get discarded. Conflicts get flagged. |
34 | 34 |
|
35 | 35 | `STAGE`, `AMEND`, `FAIL`, `STASH` never touch reducer state — they only modify the transitions list. The optimistic view updates because `selectOptimistic` re-derives it on the next read. |
|
0 commit comments