Starbeam
Starbeam is a library for building reactive data methods that mix natively
with UI frameworks such as React, Vue, Svelte or Ember.
It interoperates natively with React advise administration patterns, Svelte shops,
the Vue composition API, and Ember’s auto-tracking machine.
Starbeam Reactivity
Starbeam’s reactivity is in accordance with a in point of fact easy, but extremely efficient, knowing:
- You label your mutable advise as reactive. Individual pieces of mutable advise are known as data cells.
- You use customary functions (or getters) to compute values in accordance with your mutable advise.
- That you simply may well well flip a characteristic proper into a formula cell to robotically cache it, and this would most probably greatest recompute when the facts cells it uses change.
- You use resources to compute values that require structured cleanup.
We call this sequence of values the data universe. The data universe is repeatedly internally coherent. Whenever you mutate your advise, you may well well most probably be ready to call any characteristic that depends on the mutable advise, and that characteristic will witness an up-to-date model of the advise.
Procedure, too, are repeatedly up to this level. When you change a data cell that a formula depends on, and quiz the formula for its contemporary tag, the formula will repeatedly kind a tag that is up to this level. You by no manner must wretchedness about worn data.
The data universe becomes reactive whereas you happen to lumber it into your UI framework. Whenever you lumber it into your UI framework, any adjustments to the facts universe may well be reflected in your UI robotically.
๐ Collectively, data cells and formula cells are known as cells.
Knowledge Cells and Procedure
Making It In type
Plugging it into your UI
React
>
);
}”>
import { use } from "@starbeam/react"; import { InchCounter } from "#shared"; export characteristic MeasureInches() { const inches = use(InchCounter); return ( <> <button onClick={inches.increment}>Increment Inches</button> <div>{inches.description}</div> </> ); }
Svelte
Vue
“>
<script> import { InchCounter } from "#shared"; export default { setup() { const inches = InchCounter(); return { inches, }; }, }; script> <template> <button v-on: click="inches.increment">Increment Inchesbutton> <div>{{ inches.description }}div> template>
Sources
So what is a resource? A resource is a reactive tag, merely like our InchCounter above, that requires some cleanup. When you utilize a resource, you link it to an proprietor object, and when the proprietor object is cleaned up, the resource may well be cleaned up as effectively. In note, extra regularly than now not, the proprietor object is a side in your UI framework.
The RemoteData Handy resource
In this situation, we’ll produce a RemoteData
resource that will acquire data from a much away server.
Show cover: We produce now not call this the
acquire
resource, because a resource represents a tag now not a job with a starting and stopping level. Thanks to this, the resource is linked, 1:1, to the proprietor object.
Inner of the RemoteData
characteristic, we use the Handy resource
characteristic to present a brand unusual resource. The Handy resource
constructor takes a characteristic, which we call the “resource constructor”. The resource constructor returns a cell that represents its contemporary tag. When code uses the resource, its tag may well most probably well be the contemporary tag of the reactive tag.
A resource constructor is is named once, when the resource is first conventional. A resource constructor:
- creates interior cells to lend a hand a watch on its advise
- connects to any stateful exterior objects it needs to lend a hand a watch on, such as a network connection
- describes how to disconnect from these exterior objects when the resource is cleaned up
- returns a cell that represents the resource’s contemporary tag
๐ก A resource can use mutable advise internally, and it can work collectively with the crucial world, then again it exposes the messy outdoors world as a cell that may well be conventional in the facts universe like several assorted cell, collectively with in assorted formula and even assorted resources.
Using it in React
Now that we bear defined our data universe, we want to lumber it into React to present a reactive machine.
[username] );
if (user.kind === “loading”) {
return
;
} else if (user.kind === “error”) {
return
;
} else {
return
;
}
}”>
import { use } from "@starbeam/react"; characteristic UserCard({ username }: { username: string }) { // when `username` adjustments, we luminous up the conventional `RemoteData` resource and produce a brand unusual one. const user = use( () => RemoteData(`https://api.github.com/users/${username}`), [username] ); if (user.kind === "loading") { return <div>Loading...</div>; } else if (user.kind === "error") { return <div>Error: {user.error.message}</div>; } else { return <div>{user.data.title}</div>; } }
In precept, we may well most probably well flip RemoteData
proper into a React hook that abstracts the dependencies for once and for all. The useRemoteData
hook would gain a URL, and at any time when the URL adjustments, it would luminous up the conventional resource and produce a brand unusual one.
return use(() => RemoteData(url), [url]);
}”>
import { use } from "@starbeam/react"; characteristic useRemoteData<T>(url: string) { return use(() => RemoteData(url), [url]); }
And now we can use it in our app:
;
} else if (user.kind === “error”) {
return
;
} else {
return
;
}
}”>
import { useRemoteData } from "#hooks/a ways away-data"; characteristic UserCard({ username }: { username: string }) { const user = useRemoteData(`https://api.github.com/users/${username}`); if (user.kind === "loading") { return <div>Loading...</div>; } else if (user.kind === "error") { return <div>Error: {user.error.message}</div>; } else { return <div>{user.data.title}</div>; } }
Using it in Svelte
We can lumber the same RemoteData
resource into Svelte by turning it proper into a Svelte retailer.
Flip Firebase True into a Handy resource
Next, we’ll design a a itsy-bitsy extra complicated instance that uses Firebase. We will produce a resource for the application, which subscribes to Firebase (and unsubscribes when the application is cleaned up). That app resource will vend Firebase documents as resources, which may well be robotically up to this level when the parable adjustments, and cleaned up when their proprietor is cleaned up.
On the entire, we’re the utilization of Starbeam reactivity and possession to lend a hand a watch on many of the complexity that comes up when subscribing to Firebase documents.
const firebaseConfig = {
apiKey: “AIzaSyB-x-q-X-X-X-X-X-X-X-X-X-X-X-X-X-X-X-X-X-X-X-X-X-X-X-X-X-X-X”,
authDomain: “my-app.firebaseapp.com”,
databaseURL: “https://my-app.firebaseio.com”,
projectId: “my-app”,
storageBucket: “my-app.appspot.com”,
messagingSenderId: “123456789”,
appId: “1: 123456789:web: 123456789”,
};
const app = initializeApp(firebaseConfig);
const database = getDatabase(app);
resource.on.cleanup(() => database.goOffline());
});
export characteristic myth(db: Database, course: string) {
return Handy resource((resource) => {
const firebaseDocument = db.ref(course);
const myth = cell({ kind: “loading” });
firebaseDocument.on(“tag”, (snapshot) => {
myth.advise({ kind: “data”, data: snapshot.val() });
});
resource.on.cleanup(() => firebaseDocument.off(“tag”));
return () => myth.contemporary;
});
}”>
import { initializeApp } from "firebase/app"; import { getDatabase, kind Database } from "firebase/database"; const firebaseConfig = { apiKey: "AIzaSyB-x-q-X-X-X-X-X-X-X-X-X-X-X-X-X-X-X-X-X-X-X-X-X-X-X-X-X-X-X", authDomain: "my-app.firebaseapp.com", databaseURL: "https://my-app.firebaseio.com", projectId: "my-app", storageBucket: "my-app.appspot.com", messagingSenderId: "123456789", appId: "1: 123456789:web: 123456789", }; class Firebase { #db: Database; constructor(db: Database) { this.#db = db; } at(course: string) { return myth(this.#db, course); } } // `firebase` is defined as a generic resource, that manner it has smartly described setup and cleanup. // // It is meant to be conventional as a provider, which can most probably well carry out it a singleton *in the app*, but that manner // that *appsmay well be cleaned up, which is awfully well-known in attempting out and when rendering on the server in a // shared context. // // In brief, as every other of the utilization of module advise as a singleton, use a provider. export const firebase = Handy resource((resource) => { const firebaseConfig = { apiKey: "AIzaSyB-x-q-X-X-X-X-X-X-X-X-X-X-X-X-X-X-X-X-X-X-X-X-X-X-X-X-X-X-X", authDomain: "my-app.firebaseapp.com", databaseURL: "https://my-app.firebaseio.com", projectId: "my-app", storageBucket: "my-app.appspot.com", messagingSenderId: "123456789", appId: "1: 123456789:web: 123456789", }; const app = initializeApp(firebaseConfig); const database = getDatabase(app); resource.on.cleanup(() => database.goOffline()); }); export characteristic myth(db: Database, course: string) { return Handy resource((resource) => { const firebaseDocument = db.ref(course); const myth = cell({ kind: "loading" }); firebaseDocument.on("tag", (snapshot) => { myth.advise({ kind: "data", data: snapshot.val() }); }); resource.on.cleanup(() => firebaseDocument.off("tag")); return () => myth.contemporary; }); }
Using the Firebase Handy resource in React
Using the Firebase Handy resource in Svelte
export let course: string;
$: db = provider(firebase); $: myth = use(db.at(course));
{#if myth.kind === “loading”}
{:else}
{/if}”>
<script lang="typescript"> import { firebase } from "./firebase"; import { provider } from "@starbeam/svelte"; export let course: string; $: db = provider(firebase); $: myth = use(db.at(course)); script> {#if myth.kind === "loading"} <div>Loading...div> {:else} <div>{myth.data.title}div> {/if}
Using the Firebase Handy resource in Ember
{{#match this.myth}}
{{:when “loading”}}
{{:when “data” as |user|}}
{{/match}}
}”>
import { provider, resource } from "@starbeam/ember"; import { firebase } from "./firebase"; export default class extends Component { @provider(firebase) db; @use myth = resource(() => this.db.at(this.args.course)); {{#match this.myth}} {{:when "loading"}}Loading...{{:when "data" as |user|}}{{user.title}}{{/match}} }
Using the Firebase Handy resource in Vue
“>
<script> import { provider, resource } from "@starbeam/vue"; import { firebase } from "./firebase"; export default { setup() { const db = provider(firebase); return { myth: resource(() => db.at(this.args.course)), }; }, }; script> <template> <div v-if="document.type === 'loading'">Loading...div> <div v-else>{{ myth.data.title }}div> template>
Starbeam Ingredient Modifiers
https://github.com/maslianok/react-resize-detector
First, we’ll design a easy side modifier that will detect when a side is resized.
To witness how to utilize this, let’s design a miniature popover library that orients a popover above a goal side at its horizontal center.
{alternatives.mumble material}
);
});
}”>
/ The Popover characteristic takes a mumble material string and padding as alternatives (the alternatives may well be reactive, and the popover will update after they change). * It returns a Modifier whose tag is the rendered popover. */ characteristic Popover(alternatives: { mumble material: string; padding?: amount }) { return Modifier((side, modifier) => { const dimension = ElementSize(side); return () => ( <div className="popover" style={{ left: size.width / 2, top: -options.padding }} > {alternatives.mumble material} </div> ); }); }
And the utilization of it in our app.
Using it in Svelte
The same popover library, but for svelte:
“>
// popover.svelte <script> import ElementSize from "./ElementSize"; export let container; export let padding = 20; // use turns a Starbeam resource proper into a Svelte retailer. const dimension = use(ElementSize(container)); script> <div class="popover" style={{ left: $size.width / 2, top: -padding }} > <slot /> div>