Skip to content

Instantly share code, notes, and snippets.

@lynlevenick
Created April 28, 2020 22:12
Show Gist options
  • Save lynlevenick/12095e0d5b8212f16f165abb24edc5aa to your computer and use it in GitHub Desktop.
Save lynlevenick/12095e0d5b8212f16f165abb24edc5aa to your computer and use it in GitHub Desktop.
Implementation of `useInterval` and a basic metronome in Crank.js
/* @jsx createElement */
import { createElement } from "@bikeshaving/crank/cjs";
function* useIntervalInternal(cb: () => void) {
// unfortunately pretty convoluted to deal with
// setup and teardown? You could probably implement
// the useState and useEffect APIs here tbh
let interval = undefined;
try {
let delay = undefined;
while (true) {
const newDelay = yield;
if (newDelay !== delay) {
delay = newDelay;
if (delay != null) {
clearInterval(interval);
interval = setInterval(cb, delay);
}
}
}
} finally {
if (interval !== undefined) {
clearInterval(interval);
}
}
}
function useInterval(cb: () => void, delay?: number) {
const timer = useIntervalInternal(cb);
// not a fan of this, requires the wrapping with
// useInterval/useIntervalInternal, any clean way to work around?
// maybe some way to fuse this into the "for await" iterables?
timer.next();
if (delay != null) {
timer.next(delay);
}
return timer;
}
function* Metronome({ bpm }) {
let ticktock = false;
const timer = useInterval(() => {
ticktock = !ticktock;
this.refresh();
});
try {
for ({ bpm } of this) {
timer.next(60000 / bpm);
yield (
<div>
Metronome: {bpm} bpm => {ticktock ? "Tick" : "Tock"}
</div>
);
}
} finally {
// explicit cleanup to ensure interval is cleared on unmount
// would like a way to make this automatic
timer.return();
}
}
function* Application() {
let visible = true;
let bpm = 60;
while (true) {
yield (
<div>
<button
onclick={() => {
visible = !visible;
this.refresh();
}}
>
Toggle visibility
</button>
{visible ? <Metronome bpm={bpm} /> : undefined}
<input
type="range"
min="30"
max="480"
defaultValue={bpm}
oninput={(e) => {
bpm = +e.target.value;
this.refresh();
}}
/>
</div>
);
}
}
export default Application;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment