Composing a React app
In a React app, each feature — or a group of features — is a Task. A Task can return anything — this example uses { ui, api }.
One Task at the end — a Render Task — mounts the UI into the page. It usually reads upstream Tasks directly: every Task is known here, so a Tag adds nothing.
What the example does:
ThemeSelectis a feature — it returns the chosen theme and a way to choose it.LanguageSelectis a feature — it returns the chosen language and a way to choose it.Appearanceis a layout — through its context it reads the language from a shared Tag and the widgets from another Tag.ThemeProvideris a provider — through its context it reads the chosen theme.- The Render Task renders the
LayoutfromAppearancewrapped in theProviderfromThemeProvider. compose()wires it all; the Render Task runs last.
nanostores here is only for the task.api demo. Features can use any state manager.
import { compose, createTask, createWire, literal } from "@grlt-hub/app-compose"import { createRoot } from "react-dom/client"import { Appearance, appearanceWidgets } from "./appearance"import { LanguageSelect, ThemeSelect } from "./features"import { selectedTheme, selectedLanguage } from "./shared-tags"import { ThemeProvider } from "./theme-provider"
// page-level — reads Tasks directlyconst renderApp = createTask({ name: "render-app", run: { context: { AppearanceLayout: Appearance.result.ui.Layout, ThemeProvider: ThemeProvider.result.ui.Provider, }, fn: (ctx) => { const root = createRoot(document.getElementById("root")) root.render( <ctx.ThemeProvider> <ctx.AppearanceLayout /> </ctx.ThemeProvider>, ) }, },})
compose() .step([ThemeSelect, LanguageSelect]) // fill features data into shared tags .step([ createWire({ from: { theme: ThemeSelect.result.api.$theme, language: LanguageSelect.result.api.$language, }, to: { theme: selectedTheme, language: selectedLanguage }, }), ]) // collect feature widgets into one tag .step( createWire({ from: [ { Widget: ThemeSelect.result.ui.Widget, key: literal(ThemeSelect.name), }, { Widget: LanguageSelect.result.ui.Widget, key: literal(LanguageSelect.name), }, ], to: appearanceWidgets, }), ) .step([Appearance, ThemeProvider]) .step(renderApp) .run()