Skip to content

Instantly share code, notes, and snippets.

@wycats
Created September 2, 2022 20:03
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save wycats/96141ba6055f20d6695d3aebda97b678 to your computer and use it in GitHub Desktop.
Save wycats/96141ba6055f20d6695d3aebda97b678 to your computer and use it in GitHub Desktop.
An explanation of how Starbeam integrates with React 18's reusable state (https://github.com/reactwg/react-18/discussions/19)
import { Cell } from "@starbeam/core";
import { useSetup } from "@starbeam/react";
const SYSTEM_LOCALE = Intl.DateTimeFormat().resolvedOptions().locale;
const SYSTEM_TZ = Intl.DateTimeFormat().resolvedOptions().timeZone;
function Clock({ locale = SYSTEM_LOCALE, timeZone = SYSTEM_TZ }) {
const now = useSetup((component) => {
const now = Cell(new Date()));
// This is basically useEffect.
component.on.idle(() => {
const interval =
setInterval(() => now.set(new Date()), 1000);
return () => clearInterval(now);
});
return now;
});
function format() {
return new Intl.DateTimeFormat(locale, {
hour: "numeric",
minute: "numeric",
second: "numeric",
timeZoneName: "long",
timeZone,
})
.format(now.current);
}
return <p className="output">{format()}</p>
}
import { Cell } from "@starbeam/core";
import { useSetup } from "@starbeam/react";
const SYSTEM_LOCALE = Intl.DateTimeFormat().resolvedOptions().locale;
const SYSTEM_TZ = Intl.DateTimeFormat().resolvedOptions().timeZone;
function Clock({ locale = SYSTEM_LOCALE, timeZone = SYSTEM_TZ }) {
// useSetup runs when the component is mounted, and again when the
// component is remounted (i.e. when React runs useEffect and
// useLayoutEffect setup functions when the dependencies haven't
// changed, https://github.com/reactwg/react-18/discussions/19).
//
// In general, you can think of this as an "onMount"-style hook.
// But: you can ergonomically use it to set up state in your render
// function that is used in your JSX and managed in an effect, since
// you'll get a fresh copy of the setup state every time React
// remounts the component.
const now = useSetup((component) => {
// This state will be available on the first render, and will be
// updated in the effect below. This is a critical aspect of what
// we need here:
//
// - A value that we can use right away during initial render
// - To defer the stateful setup code until useEffect timing, because
// otherwise we're not guaranteed that the cleanup code will run
const now = Cell(new Date()));
// This is basically useEffect. When React unmounts this component,
// the cleanup function (of course) gets called. When React
// *re-mounts* this component, the whole setup function gets invoked
// again, creating a fresh `now` for the effect to work with.
component.on.idle(() => {
const interval =
setInterval(() => now.set(new Date()), 1000);
return () => clearInterval(now);
});
return now;
});
// This is a normal function. It gets created on every render, which
// means that whenever React runs the render function, you get a new
// copy of the `format` function that closes over the correct `locale`
// and `timeZone`.
function format() {
return new Intl.DateTimeFormat(locale, {
hour: "numeric",
minute: "numeric",
second: "numeric",
timeZoneName: "long",
timeZone,
})
// this function can refer to the `now` cell,
// which is a stable value that will be updated
// below.
.format(now.current);
}
return <p className="output">{format()}</p>
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment