Skip to content

Commit 9a3a062

Browse files
authored
feat: do not add to history when the present state did not change (#337)
1 parent 82c2c9a commit 9a3a062

File tree

4 files changed

+57
-14
lines changed

4 files changed

+57
-14
lines changed

src/createReducer.spec.ts

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import {
44
CountActions,
55
countReducer,
66
increment,
7+
noop,
78
} from "./fixtures"
89

910
describe("create reducer", () => {
@@ -62,12 +63,25 @@ describe("create reducer", () => {
6263

6364
const untrackedState = reducer(
6465
{ past: () => [0], present: () => 1, future: () => [2] },
65-
increment()
66+
increment(),
6667
)
6768

6869
expect(untrackedState.past()).toEqual([0])
6970
expect(untrackedState.present()).toEqual(2)
7071
expect(untrackedState.future()).toEqual([2])
7172
})
73+
74+
it("does not change past, present, and future when the state did not update", () => {
75+
const reducer = createReducer(countReducer)
76+
77+
const updatedState = reducer(
78+
{ past: () => [0], present: () => 1, future: () => [2] },
79+
noop(),
80+
)
81+
82+
expect(updatedState.past()).toEqual([0])
83+
expect(updatedState.present()).toEqual(1)
84+
expect(updatedState.future()).toEqual([2])
85+
})
7286
})
7387
})

src/createReducer.ts

Lines changed: 18 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ export type UndoRedoState<Present> = {
66

77
export type PresentReducer<Present, Actions> = (
88
state: Present,
9-
action: Actions
9+
action: Actions,
1010
) => Present
1111

1212
export type UndoRedoOptions<Actions> = {
@@ -15,7 +15,7 @@ export type UndoRedoOptions<Actions> = {
1515

1616
export type UndoRedoReducer<Present, Actions> = (
1717
state: UndoRedoState<Present>,
18-
action: Actions
18+
action: Actions,
1919
) => UndoRedoState<Present>
2020

2121
const enum UndoRedoActionTypes {
@@ -37,11 +37,11 @@ const trackAll = () => true
3737

3838
export function createReducer<Present, Actions extends {}>(
3939
presentReducer: PresentReducer<Present, Actions>,
40-
{ track = trackAll }: UndoRedoOptions<Actions> = {}
40+
{ track = trackAll }: UndoRedoOptions<Actions> = {},
4141
): UndoRedoReducer<Present, UndoRedoActions<Actions>> {
4242
return function reducer(
4343
state: UndoRedoState<Present>,
44-
action: UndoRedoActions<Actions>
44+
action: UndoRedoActions<Actions>,
4545
): UndoRedoState<Present> {
4646
if ("type" in action) {
4747
if (action.type === UndoRedoActionTypes.UNDO) {
@@ -68,6 +68,7 @@ export function createReducer<Present, Actions extends {}>(
6868
}
6969

7070
const isTrackableAction = track(action)
71+
7172
if (!isTrackableAction) {
7273
return {
7374
past: () => state.past(),
@@ -76,12 +77,22 @@ export function createReducer<Present, Actions extends {}>(
7677
}
7778
}
7879

79-
const past = [state.present(), ...state.past()]
80-
const present = presentReducer(state.present(), action)
80+
const present = state.present()
81+
const nextPresent = presentReducer(state.present(), action)
82+
83+
if (present === nextPresent) {
84+
return {
85+
past: () => state.past(),
86+
present: () => present,
87+
future: () => state.future(),
88+
}
89+
}
90+
91+
const past = [present, ...state.past()]
8192

8293
return {
8394
past: () => past,
84-
present: () => present,
95+
present: () => nextPresent,
8596
future: () => [],
8697
}
8798
}

src/fixtures/countReducer.ts

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import invariant from "tiny-invariant"
33
export enum CountActionTypes {
44
INCREMENT = "@@count/increment",
55
DECREMENT = "@@count/decrement",
6+
NOOP = "@@count/noop",
67
}
78

89
type IncrementAction = {
@@ -13,7 +14,11 @@ type DecrementAction = {
1314
type: CountActionTypes.DECREMENT
1415
}
1516

16-
export type CountActions = IncrementAction | DecrementAction
17+
type NoOpAction = {
18+
type: CountActionTypes.NOOP
19+
}
20+
21+
export type CountActions = IncrementAction | DecrementAction | NoOpAction
1722

1823
export const countReducer = (state: number, action: CountActions): number => {
1924
invariant(state != null, "Count reducer needs an initial state")
@@ -23,12 +28,19 @@ export const countReducer = (state: number, action: CountActions): number => {
2328
return state + 1
2429
case CountActionTypes.DECREMENT:
2530
return state - 1
31+
case CountActionTypes.NOOP:
32+
return state
2633
default:
2734
invariant(false, "Count reducer received an unknown action.")
2835
}
2936
}
3037

31-
export const increment = (): IncrementAction =>
32-
({ type: CountActionTypes.INCREMENT } as const)
33-
export const decrement = (): DecrementAction =>
34-
({ type: CountActionTypes.DECREMENT } as const)
38+
export const increment = (): IncrementAction => ({
39+
type: CountActionTypes.INCREMENT,
40+
})
41+
42+
export const decrement = (): DecrementAction => ({
43+
type: CountActionTypes.DECREMENT,
44+
})
45+
46+
export const noop = (): NoOpAction => ({ type: CountActionTypes.NOOP })

src/fixtures/index.ts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1,7 @@
1-
export { countReducer, CountActions, CountActionTypes, increment } from "./countReducer"
1+
export {
2+
countReducer,
3+
CountActions,
4+
CountActionTypes,
5+
increment,
6+
noop,
7+
} from "./countReducer"

0 commit comments

Comments
 (0)