Created
April 28, 2020 22:12
-
-
Save lynlevenick/12095e0d5b8212f16f165abb24edc5aa to your computer and use it in GitHub Desktop.
Implementation of `useInterval` and a basic metronome in Crank.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* @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