-
-
Notifications
You must be signed in to change notification settings - Fork 3
Expand file tree
/
Copy pathStore.ts
More file actions
90 lines (78 loc) · 2.91 KB
/
Copy pathStore.ts
File metadata and controls
90 lines (78 loc) · 2.91 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
import * as React from "react";
import { startTransition } from "react";
import { ISource } from "../types";
import Emitter from "./Emitter";
const sharedReactInternals: { T: unknown } =
React.__CLIENT_INTERNALS_DO_NOT_USE_OR_WARN_USERS_THEY_CANNOT_UPGRADE as any;
function reactTransitionIsActive() {
return !!sharedReactInternals.T;
}
export interface ReactStore<S, A = never> {
getState(): S;
getCommittedState(): S;
handleUpdate(action: A): void;
subscribe(listener: () => void): () => void;
commit(state: S): void;
}
export class Store<S, A> extends Emitter<[]> implements ReactStore<S, A> {
source: ISource<S, A>;
state: S;
committedState: S;
constructor(source: ISource<S, A>) {
super();
this.source = source;
this.state = source.getState();
this.committedState = source.getState();
}
commit(state: S) {
this.committedState = state;
}
getCommittedState(): S {
return this.committedState;
}
getState(): S {
return this.state;
}
handleUpdate(action: A) {
const noPendingTransitions = this.committedState === this.state;
this.state = this.source.getState();
if (reactTransitionIsActive()) {
// For transition updates, everything is simple. Just notify all readers
// of the new state.
this.notify();
} else {
// For sync updates, we must consider if we need to juggle multiple state
// updates.
// If there are no pending transition updates, things are very similar to
// a transition update except that we can proactively mark the new state
// as committed.
if (noPendingTransitions) {
this.committedState = this.state;
this.notify();
} else {
// If there are pending transition updates, we must ensure we compute
// an additional new states: This update applied on top of the current
// committed state.
const newState = this.state;
// React's rebasing semantics mean readers will expect to see this
// update applied on top of the currently committed state sync.
this.committedState = this.source.reducer(this.committedState, action);
// Temporarily set the state so that readers during this notify read the
// new committed state.
this.state = this.committedState;
this.notify();
// Now that we've triggered the sync updates, we need to ensure the
// pending transition update now goes to the correct new state. We reset
// the state to point to the new transition state and trigger a set of
// updates inside a transition.
// With existing transition semantics this should result in these
// updates entangling with the previous transition and that transition
// will now include this state instead of the previously pending state.
this.state = newState;
startTransition(() => {
this.notify();
});
}
}
}
}