squiggle/packages/components/src/lib/hooks/useRunnerState.ts
2022-07-27 12:12:03 +04:00

93 lines
1.9 KiB
TypeScript

import { useEffect, useReducer } from "react";
type State = {
autorunMode: boolean;
renderedCode: string;
isRunning: boolean;
executionId: number;
};
const buildInitialState = (code: string) => ({
autorunMode: true,
renderedCode: code,
isRunning: false,
executionId: 0,
});
type Action =
| {
type: "SET_AUTORUN_MODE";
value: boolean;
code: string;
}
| {
type: "PREPARE_RUN";
}
| {
type: "RUN";
code: string;
}
| {
type: "STOP_RUN";
};
const reducer = (state: State, action: Action): State => {
switch (action.type) {
case "SET_AUTORUN_MODE":
return {
...state,
autorunMode: action.value,
};
case "PREPARE_RUN":
return {
...state,
isRunning: true,
};
case "RUN":
return {
...state,
renderedCode: action.code,
executionId: state.executionId + 1,
};
case "STOP_RUN":
return {
...state,
isRunning: false,
};
}
};
export const useRunnerState = (code: string) => {
const [state, dispatch] = useReducer(reducer, buildInitialState(code));
useEffect(() => {
if (state.isRunning) {
if (state.renderedCode !== code) {
dispatch({ type: "RUN", code });
} else {
dispatch({ type: "STOP_RUN" });
}
}
}, [state.isRunning, state.renderedCode, code]);
const run = () => {
// The rest will be handled by dispatches above on following renders, but we need to update the spinner first.
dispatch({ type: "PREPARE_RUN" });
};
if (state.autorunMode && state.renderedCode !== code && !state.isRunning) {
run();
}
return {
run,
autorunMode: state.autorunMode,
renderedCode: state.renderedCode,
isRunning: state.isRunning,
executionId: state.executionId,
setAutorunMode: (newValue: boolean) => {
dispatch({ type: "SET_AUTORUN_MODE", value: newValue, code });
},
};
};