tcmts.react
react
React's routes, links and data for a tcm app, as a library: tcm ships it, so tcm lib add tcm react adds the release
your tcm has, with no network (and tcm lib upgrade react the one a newer tcm has). It's also announced by tcm.codes
(tcm lib add @tcm.codes/react), and comes by hash from any tcm server that has it. It's a tcm library, not an npm package: it holds everything
its code uses but React itself, so a codebase that adds it has every definition named.
import { Pages, Stylesheet, useAppLinks, useData, useLocation } from "#codebase/app/lib/react";
import { styles } from "#codebase/app";
export const App = () => {
useAppLinks();
return (
<>
<Stylesheet css={styles} />
<Pages routes={{ "/": Home, "/n/:id": Note }} />
</>
);
};
const Note = ({ params }: { params: { id: string } }) => {
const note = useData(`note:${params.id}`, () => readNote(params.id), { keep: true });
return <h1>{note.value?.title}</h1>;
};
What it has
- The location:
useLocation()(the path, query and fragment; re-renders when they change, and reads the page's URL in the server's first paint),navigate(to),useNavigating()while a navigation renders,match(pattern, path)for routes like/s/:owner/:key, andhref(path), a path's address under the app's base. - Links:
useAppLinks()follows the page's links to the app without reloading it. - Data:
useData(key, read)reads something once per key while the page is open;refreshreads it again. - Updates:
useAppUpdate()says when a PWA's new version is ready. - Pages:
<Pages routes={…} />renders the route the path matches, keeps the last few pages mounted but hidden (React's<Activity>), and renders a page a link to it is pointed at ahead. - Stylesheets:
<Stylesheet css={…} />puts CSS in the page through React's stylesheets; one of the codebase is linked to as the file the app's build serves (its manifest gives it thestylesheetrole).
A navigation is a transition (state is React state, never useSyncExternalStore): what's on screen stays until the
next page is ready, and <ViewTransition> can animate it (navigate, navigate-forward, navigate-back).
tcm's runtime
It reaches tcm's runtime through the page's globals the runtime sets (__TCM_BASE__, __TCM_STYLESHEETS__,
__TCM_TREES__, __tcmPrefetch, __tcmUpdate): version 1 of tcm's host protocol, as its manifest says. A tcm that
doesn't serve that version won't build an app using it, rather than build one that breaks.
Its versions follow semver from its API, as any package's do (releases).
- test/
- 4 definitions
Types
- Data type
- Something read for a component, by key.
- PageProps type
- What a page of `<Pages>` is given: its route's parameters (`/p/:id` → `{ id }`) and the query.
- Where type
- Where the page is: its path, query and fragment.
Browser
- Pages (props: { routes: Record<string, (props: PageProps) => Promise<ReactNode> | ReactNode>; keep?: numb…
- The page the path matches (the first of `routes`, by `match`; `notFound` if none). The last `keep` pages left (3) stay mounted but hidden (React's `<Activity>`), so going back shows one at once; the page a link to is pointed at is rendered ahead, hidden. The server's first paint is the page shown, alone.
- createRouter (page: typeof globalThis) => { useLocation: () => Where; useNavigating: () => boolean; navigate: (t…
- What a page's routes, links and data keep while it's open, over the page's globals (`globalThis` in the browser; the server's first paint lacks some of them): the location and its watchers, the data read, the pages kept. One of them, router, is what the library's hooks and `<Pages>` use.
- navigate (to: string, options?: { replace?: boolean | undefined; scroll?: boolean | undefined; } | undefined…
- Go to a path of the app without reloading the page (another site's URL is loaded), in a transition typed `navigate` and `navigate-forward` or `navigate-back`: what's on screen stays until the next page is ready. `replace` doesn't add a history entry; `scroll` (the default unless `replace`) scrolls to the top, or to the fragment.
- refresh (prefix?: string | undefined) => Promise<void>
- Read again what's shown whose key starts with `prefix` (all of it, by default), after a change; what isn't shown is read when it is.
- router { useLocation: () => Where; useNavigating: () => boolean; navigate: (to: string, options?: { replac…
- The page's routes, links and data: the one router the hooks and `<Pages>` share.
- useAppLinks (isApp?: ((path: string) => boolean) | undefined) => void
- Follow the page's links to the app (`<a href>` anywhere on it) without reloading the page. Links with a modifier key, a `target`, `download` or `rel="external"` are left to the browser, and so is a path `isApp` says isn't the app's.
- useAppUpdate () => { ready: boolean; reload(): void; }
- Whether a new version of the app is ready: a PWA's service worker took the new build in (`reload` loads it now). Always false for an app that isn't a PWA, and until the page is hydrated.
- useData <T>(key: string | undefined, read: () => Promise<T>, options?: { keep?: boolean | undefined; } | un…
- Read something once per key while the page is open: `key` names what `read` reads. What's read already is shown at once; `keep` shows the previous key's while the next is read. An undefined key reads nothing.
- useLocation () => Where
- Where the page is; the component re-renders when it changes. While the server renders the first paint, the page's URL. Under a base, `path` is within it: `/` is the base.
- useNavigating () => boolean
- Whether a navigation (`navigate`, back or forward) is still rendering: the next page is on its way, and the last one is still on screen.
Shared
- Stylesheet ({ css, precedence, }: { css: string; precedence?: string | undefined; }) => ReactNode
- CSS for the page, through React's stylesheets: once however many components render it, kept when they're gone, ahead of the page's content, grouped by `precedence` (`"default"`). A stylesheet of the codebase (a `.css` file) is served by the app's build as a file of its own and linked to (`__TCM_STYLESHEETS__`, filled as its modules load): the page waits for it to load rather than show unstyled, and the browser keeps it. Other CSS is written into the page.
- href (path: string) => string
- A path of the app as a link's address: under the app's base, if it's served under one (`/s/1` → `/examples/notes/s/1`); one under it already (`location.pathname`), and other URLs, as they are. `navigate` takes either.
- manifest { name: string; npm: boolean; host: number; roles: { Stylesheet: string; }; }
- React's routes, links and data for a tcm app, as a library: the page's location and navigation, following its links, data read once per key, a PWA's update, pages kept and rendered ahead, and stylesheets linked to. It reaches tcm's runtime through the page's globals (host protocol 1); `Stylesheet` has the `stylesheet` role, so the build links the stylesheet files it's given.
- match (pattern: string, path: string) => Record<string, string> | undefined
- A path's parameters if it matches the pattern (`/s/:owner/:key`, `/docs/*`), decoded; undefined if it doesn't.
Files
- README file