import { useCallback, useContext, useEffect, useMemo, useRef, useState, } from 'react'; import { RunnerContext } from './context'; type BlockProps = { worker: Worker; action: string; presenter?: React.FC; }; const id = (function* () { let i = 0; while (true) { yield i++; } })(); const Block: React.FC = ({ worker, action, presenter: Presenter, }) => { const currentId = useRef(id.next().value); const { vars } = useContext(RunnerContext); const [output, setOutput] = useState(); const [error, setError] = useState(); const [running, setRunning] = useState(); const [duration, setDuration] = useState(); const view = useMemo(() => { if (error) { return error.toString(); } if (Presenter) { return ; } return JSON.stringify(output, null, 2); }, [output, error, Presenter]); const runBlock = useCallback(async () => { setRunning(true); setError(undefined); setOutput(undefined); try { worker.postMessage({ type: 'run', action, vars, id: currentId.current, }); } catch (error) { setError(error); } setRunning(false); }, [worker, vars, action]); useEffect(() => { const listener = (event: MessageEvent) => { const { type, payload, id, duration } = event.data; if (id !== currentId.current) { return; } setDuration(duration); setRunning(false); if (type === 'output') { setOutput(payload); } if (type === 'error') { setError(payload); } }; worker.addEventListener('message', listener); return () => { worker.removeEventListener('message', listener); }; }, [worker]); return (
{duration &&
Duration: {duration.toFixed(2)}ms
} {running &&
Running...
} {view}
); }; export { Block };