Skip to content

Commit 7f2a93b

Browse files
committed
Add machine.value with single snapshot of data
1 parent 36029c7 commit 7f2a93b

File tree

2 files changed

+79
-23
lines changed

2 files changed

+79
-23
lines changed

src/index.test.ts

Lines changed: 44 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -76,24 +76,45 @@ describe("Machine with entry and exit actions", () => {
7676

7777
test("sending events", async () => {
7878
const loader = start(Loader);
79-
expect(loader.current).toEqual("idle");
79+
expect(loader.value).toMatchObject({
80+
change: 0,
81+
state: "idle",
82+
actions: []
83+
});
84+
expect(loader.value).toBe(loader.value);
8085
expect(loader.changeCount).toEqual(0);
86+
expect(loader.current).toEqual("idle");
87+
88+
const valueA = loader.value;
8189

8290
loader.next("NOOP");
8391
expect(loader.current).toEqual("idle");
8492
expect(loader.changeCount).toEqual(0);
93+
expect(loader.value).toBe(valueA);
8594

8695
const transitionResult = loader.next("FETCH");
8796
expect(fetch).toHaveBeenCalledWith("https://example.org/");
88-
expect(transitionResult.actions).toEqual([
89-
{ type: "entry", f: fetchData },
90-
]);
97+
expect(transitionResult.value).toMatchObject({
98+
change: 1,
99+
state: "loading",
100+
actions: [
101+
{ type: "entry", f: fetchData }
102+
]
103+
});
104+
expect(loader.value).toMatchObject({
105+
change: 1,
106+
state: "loading",
107+
actions: [
108+
{ type: "entry", f: fetchData }
109+
]
110+
});
111+
expect(loader.value).not.toBe(valueA);
91112
expect(loader.current).toEqual("loading");
92113
expect(loader.changeCount).toEqual(1);
93114
expect(finishedLoading).toHaveBeenCalledTimes(0);
94115

95-
await expect(loader.results).resolves.toEqual({ fetchData: 42 });
96-
await expect(Promise.resolve(transitionResult)).resolves.toEqual({
116+
await expect(loader.value.results).resolves.toEqual({ fetchData: 42 });
117+
await expect(Promise.resolve(transitionResult.value.results)).resolves.toEqual({
97118
fetchData: 42,
98119
});
99120
expect(finishedLoading).toHaveBeenCalledTimes(1);
@@ -118,19 +139,28 @@ describe("Machine with entry and exit actions", () => {
118139

119140
test("sending events", async () => {
120141
const loader = start(Loader);
121-
expect(loader.current).toEqual("idle");
142+
expect(loader.value).toMatchObject({
143+
change: 0,
144+
state: "idle",
145+
actions: []
146+
});
122147

123148
const transitionResult = loader.next("FETCH");
124149
expect(fetch).toHaveBeenCalledTimes(1);
125150
expect(fetch).toHaveBeenLastCalledWith("https://example.org/");
126-
expect(transitionResult.actions).toEqual([
151+
expect(transitionResult.value.actions).toEqual([
127152
{ type: "entry", f: fetchData },
128153
]);
129-
expect(loader.current).toEqual("loading");
130-
expect(loader.changeCount).toEqual(1);
154+
expect(loader.value).toMatchObject({
155+
change: 1,
156+
state: "loading",
157+
actions: [
158+
{ type: "entry", f: fetchData },
159+
]
160+
});
131161

132-
await expect(loader.results).rejects.toEqual(new Error("Failed!"));
133-
await expect(Promise.resolve(transitionResult)).rejects.toEqual(
162+
await expect(loader.value.results).rejects.toEqual(new Error("Failed!"));
163+
await expect(Promise.resolve(transitionResult.value.results)).rejects.toEqual(
134164
new Error("Failed!")
135165
);
136166
expect(loader.changeCount).toEqual(2);
@@ -211,7 +241,7 @@ describe.skip("Fetch with abort signal", () => {
211241

212242
const transitionResult = loader.next("FETCH");
213243
expect(fetch).toHaveBeenCalledWith("https://example.org/");
214-
expect(transitionResult.actions).toEqual([
244+
expect(transitionResult.value.actions).toEqual([
215245
{ type: "entry", f: fetchData },
216246
]);
217247
expect(loader.current).toEqual("loading");
@@ -249,7 +279,7 @@ describe.skip("Fetch with abort signal", () => {
249279
const transitionResult = loader.next("FETCH");
250280
expect(fetch).toHaveBeenCalledTimes(1);
251281
expect(fetch).toHaveBeenLastCalledWith("https://example.org/");
252-
expect(transitionResult.actions).toEqual([
282+
expect(transitionResult.value.actions).toEqual([
253283
{ type: "entry", f: fetchData },
254284
]);
255285
expect(loader.current).toEqual("loading");

src/index.ts

Lines changed: 35 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -123,17 +123,22 @@ export function readContext(contextName: string | symbol): ReadContext {
123123
return { type: "readContext", contextName };
124124
}
125125

126-
export interface MachineInstance extends Iterator<null | string | Record<string, string>, void, string | symbol> {
126+
interface MachineValue {
127+
readonly change: number;
128+
readonly state: null | string | Record<string, string>;
129+
readonly actions: Array<EntryAction | ExitAction>;
130+
readonly results: null | Promise<unknown>;
131+
}
132+
133+
export interface MachineInstance extends Iterator<MachineValue, void, string | symbol> {
134+
readonly value: MachineValue;
127135
readonly changeCount: number;
128136
readonly current: null | string | Record<string, string>;
129137
readonly results: null | Promise<unknown>;
130138
readonly accumulations: Map<symbol | string, Array<symbol | string | Event>>;
131139
readonly done: boolean;
132140
readonly signal: AbortSignal;
133-
next(arg: string | symbol): IteratorResult<null | string | Record<string, string>> &
134-
PromiseLike<any> & {
135-
actions: Array<EntryAction | ExitAction>;
136-
};
141+
next(arg: string | symbol): IteratorResult<MachineValue>;
137142
abort(): void;
138143
}
139144

@@ -539,10 +544,34 @@ export function start(
539544
const current = instance.current;
540545
return current !== null ? current[rootName] : null;
541546
}
547+
548+
let _cachedValue: undefined | MachineValue;
549+
function getValue() {
550+
if (_cachedValue?.change === _changeCount) {
551+
return _cachedValue;
552+
}
553+
554+
let resultCache: undefined | Promise<unknown> = undefined;
555+
_cachedValue = Object.freeze({
556+
change: _changeCount,
557+
state: getCurrent(),
558+
actions: instance.actions,
559+
get results() {
560+
if (resultCache === undefined) {
561+
resultCache = instance.results;
562+
}
563+
return resultCache;
564+
}
565+
});
566+
return _cachedValue;
567+
}
542568

543569
_changeCount = 0;
544570

545571
return {
572+
get value() {
573+
return getValue();
574+
},
546575
get changeCount() {
547576
return _changeCount;
548577
},
@@ -560,11 +589,8 @@ export function start(
560589
},
561590
next(event: string | symbol) {
562591
instance.receive(event);
563-
const promise = instance.results;
564592
return {
565-
value: instance.current !== null ? instance.current[rootName] : null,
566-
actions: instance.actions,
567-
then: promise?.then.bind(promise),
593+
value: getValue(),
568594
done: false,
569595
};
570596
},

0 commit comments

Comments
 (0)