diff --git a/packages/components/src/components/SquiggleChart.tsx b/packages/components/src/components/SquiggleChart.tsx index 10e4e22c..088b49bf 100644 --- a/packages/components/src/components/SquiggleChart.tsx +++ b/packages/components/src/components/SquiggleChart.tsx @@ -14,6 +14,8 @@ import { SquiggleViewer } from "./SquiggleViewer"; export interface SquiggleChartProps { /** The input string for squiggle */ code?: string; + /** Allows to re-run the code if code hasn't changed */ + executionId?: number; /** If the output requires monte carlo sampling, the amount of samples */ sampleCount?: number; /** The amount of points returned to draw the distribution */ @@ -59,6 +61,7 @@ const defaultOnChange = () => {}; export const SquiggleChart: React.FC = React.memo( ({ code = "", + executionId = 0, environment, onChange = defaultOnChange, // defaultOnChange must be constant, don't move its definition here height = 200, diff --git a/packages/components/src/components/SquigglePlayground.tsx b/packages/components/src/components/SquigglePlayground.tsx index a40b1efd..c7e63f4e 100644 --- a/packages/components/src/components/SquigglePlayground.tsx +++ b/packages/components/src/components/SquigglePlayground.tsx @@ -269,12 +269,19 @@ export const SquigglePlayground: FC = ({ [vars.sampleCount, vars.xyPointLength] ); - const { run, autorunMode, setAutorunMode, isRunning, renderedCode } = - useRunnerState(code); + const { + run, + autorunMode, + setAutorunMode, + isRunning, + renderedCode, + executionId, + } = useRunnerState(code); const squiggleChart = ( ({ +const buildInitialState = (code: string): State => ({ autorunMode: true, renderedCode: code, - isRunning: false, - executionId: 0, + runningState: "none", + executionId: 1, }); type Action = @@ -41,18 +42,19 @@ const reducer = (state: State, action: Action): State => { case "PREPARE_RUN": return { ...state, - isRunning: true, + runningState: "prepared", }; case "RUN": return { ...state, + runningState: "run", renderedCode: action.code, executionId: state.executionId + 1, }; case "STOP_RUN": return { ...state, - isRunning: false, + runningState: "none", }; } }; @@ -61,21 +63,23 @@ 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" }); - } + if (state.runningState === "prepared") { + dispatch({ type: "RUN", code }); + } else if (state.runningState === "run") { + dispatch({ type: "STOP_RUN" }); } - }, [state.isRunning, state.renderedCode, code]); + }, [state.runningState, 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) { + if ( + state.autorunMode && + state.renderedCode !== code && + state.runningState === "none" + ) { run(); } @@ -83,7 +87,7 @@ export const useRunnerState = (code: string) => { run, autorunMode: state.autorunMode, renderedCode: state.renderedCode, - isRunning: state.isRunning, + isRunning: state.runningState !== "none", executionId: state.executionId, setAutorunMode: (newValue: boolean) => { dispatch({ type: "SET_AUTORUN_MODE", value: newValue, code }); diff --git a/packages/components/src/lib/hooks/useSquiggle.ts b/packages/components/src/lib/hooks/useSquiggle.ts index 03b8c69d..4165a7be 100644 --- a/packages/components/src/lib/hooks/useSquiggle.ts +++ b/packages/components/src/lib/hooks/useSquiggle.ts @@ -9,6 +9,7 @@ import { useEffect, useMemo } from "react"; type SquiggleArgs> = { code: string; + executionId?: number; bindings?: bindings; jsImports?: jsImports; environment?: environment; @@ -21,7 +22,15 @@ const useSquiggleAny = >( ) => { const result: T = useMemo( () => f(args.code, args.bindings, args.environment, args.jsImports), - [f, args.code, args.bindings, args.environment, args.jsImports] + // eslint-disable-next-line react-hooks/exhaustive-deps + [ + f, + args.code, + args.bindings, + args.environment, + args.jsImports, + args.executionId, + ] ); const { onChange } = args;