Skip to content

Commit 858914e

Browse files
authored
react-experiment: fix logger's run not being completed (#165)
1 parent e3774ba commit 858914e

File tree

3 files changed

+34
-20
lines changed

3 files changed

+34
-20
lines changed

.changeset/dull-buckets-decide.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@lightmill/react-experiment': patch
3+
---
4+
5+
Fix logger's run not being marked as completed under run completion.

packages/react-experiment/src/run.tsx

Lines changed: 4 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ export type RunProps<T extends RegisteredTask> = {
1616
logger?: Logger;
1717
confirmBeforeUnload?: boolean;
1818
cancelRunOnUnload?: boolean;
19+
completeRunOnCompletion?: boolean;
1920
};
2021

2122
// This component uses explicit return type to prevent the function from
@@ -26,14 +27,16 @@ export function Run<T extends RegisteredTask>({
2627
elements: { tasks, completed, loading, crashed },
2728
confirmBeforeUnload = true,
2829
cancelRunOnUnload = true,
30+
completeRunOnCompletion = true,
2931
}: RunProps<T>): JSX.Element | null {
3032
// Logger and timeline are not controlled. We make sure any change to them
3133
// is ignored, and the previous value is used instead.
3234
let timelineRef = React.useRef(timelineProp);
3335
let loggerRef = React.useRef(loggerProp ?? null);
3436
let timelineState = useManagedTimeline(
3537
timelineRef.current,
36-
loggerRef.current
38+
loggerRef.current,
39+
{ cancelRunOnUnload, completeRunOnCompletion }
3740
);
3841
let [loggerState, setLoggerState] = React.useState<{
3942
status: 'error' | 'ok';
@@ -60,18 +63,6 @@ export function Run<T extends RegisteredTask>({
6063
confirmBeforeUnload && timelineState.status !== 'completed'
6164
);
6265

63-
// When the page is closed, one may want to mark the run as canceled.
64-
React.useEffect(() => {
65-
if (!cancelRunOnUnload) return;
66-
let unloadHandler = () => {
67-
loggerRef.current?.cancelRun?.();
68-
};
69-
globalThis.addEventListener('unload', unloadHandler);
70-
return () => {
71-
globalThis.removeEventListener('unload', unloadHandler);
72-
};
73-
}, [cancelRunOnUnload]);
74-
7566
if (loggerState.status === 'error') {
7667
if (loggerState.error == null) {
7768
throw new Error('Could not add log to logger.');

packages/react-experiment/src/timeline.ts

Lines changed: 25 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -23,9 +23,9 @@ type TimelineAction<T> =
2323
| { type: 'run-started' }
2424
| { type: 'task-started'; task: T; onTaskCompleted: () => void }
2525
| { type: 'task-completed' }
26-
| { type: 'run-completed' }
26+
| { type: 'all-tasks-completed' }
2727
| { type: 'run-canceled' }
28-
| { type: 'logger-flushed' }
28+
| { type: 'run-completed' }
2929
| { type: 'logger-error'; error: Error };
3030

3131
export type Logger = {
@@ -51,11 +51,11 @@ function timelineReducer<T extends BaseTask>(
5151
onTaskCompleted: action.onTaskCompleted,
5252
};
5353
case 'task-completed':
54-
case 'run-completed':
54+
case 'all-tasks-completed':
5555
return { status: 'loading', task: null };
5656
case 'run-canceled':
5757
return { status: 'canceled', task: null };
58-
case 'logger-flushed':
58+
case 'run-completed':
5959
return { status: 'completed', task: null };
6060
case 'logger-error':
6161
return { status: 'crashed', task: null, error: action.error };
@@ -64,12 +64,29 @@ function timelineReducer<T extends BaseTask>(
6464

6565
export default function useManagedTimeline<Task extends BaseTask>(
6666
timeline: Timeline<Task> | null,
67-
logger: Logger | null
67+
logger: Logger | null,
68+
{
69+
cancelRunOnUnload,
70+
completeRunOnCompletion,
71+
}: { cancelRunOnUnload: boolean; completeRunOnCompletion: boolean }
6872
): TimelineState<Task> {
6973
const [state, dispatch] = React.useReducer(timelineReducer<Task>, {
7074
status: 'idle',
7175
task: null,
7276
});
77+
78+
// When the page is closed, one may want to mark the run as canceled.
79+
React.useEffect(() => {
80+
if (!cancelRunOnUnload) return;
81+
let unloadHandler = () => {
82+
logger?.cancelRun?.();
83+
};
84+
globalThis.addEventListener('unload', unloadHandler);
85+
return () => {
86+
globalThis.removeEventListener('unload', unloadHandler);
87+
};
88+
}, [cancelRunOnUnload, logger]);
89+
7390
React.useEffect(() => {
7491
let hasEnded = false;
7592
async function doRun() {
@@ -99,10 +116,11 @@ export default function useManagedTimeline<Task extends BaseTask>(
99116
},
100117
});
101118
if (hasEnded) return;
102-
dispatch({ type: 'run-completed' });
119+
dispatch({ type: 'all-tasks-completed' });
103120
await logger?.flush();
121+
await logger?.completeRun();
104122
if (hasEnded) return;
105-
dispatch({ type: 'logger-flushed' });
123+
dispatch({ type: 'run-completed' });
106124
hasEnded = true;
107125
}
108126
doRun();

0 commit comments

Comments
 (0)