JSFiddle - React, Tailwind, and code Playground
by Josh Pullen
HTML
<!--
Create timer that keeps you on track to do n questions in m minutes
(default 23 questions in 35 minutes)
-->
JavaScript
import { html, render, useReducer, useEffect, useCallback } from "https://unpkg.com/htm/preact/standalone.module.js";
function App() {
const { time, status, start, pause, reset } = useStopwatch();
return html`
<div style=${{ width: "100vw", height: "100vh", display: "flex", flexDirection: "column", alignItems: "center", justifyContent: "center", overflowY: "auto" }}>
<h1>LSAT Timer</h1>
<div>{}</div>
</div>
`;
}
function getTime(state) {
switch (state.status) {
case "off":
return state.startValue;
case "on":
return state.startValue + (Date.now() - state.startTime) / 1000;
default:
return 0;
}
}
function reducer(state, action) {
switch (action.type) {
case "START":
return {
status: "on",
startTime: Date.now(),
startValue: getTime(state)
}
case "PAUSE":
return {
status: "off",
startValue: getTime(state)
}
case "RESET":
default:
return {
status: "off",
startValue: 0
}
}
}
function useConstantRerender(enabled = true) {
const [, forceRerender] = useReducer(v => !v, true);
useEffect(() => {
if (enabled) {
let killed = false;
const callback = () => {
forceRerender();
if (!killed) {
requestAnimationFrame(callback);
}
}
requestAnimationFrame(callback);
return () => {
killed = true;
}
}
}, [enabled]);
}
function useStopwatch() {
const [state, dispatch] = useReducer(reducer, { status: "off", startValue: 0 });
const start = useCallback(() => {
dispatch({ type: "START" });
}, [dispatch]);
const pause = useCallback(() => {
dispatch({ type: "PAUSE" });
}, [dispatch]);
const reset = useCallback(() => {
dispatch({ type: "RESET" });
}, [dispatch]);
const time = getTime(state);
useConstantRerender(state.status === "on");
return { time, status, start, pause, reset...