type RemoteDataState<T> = "loading" | ["data", T] | ["error", unknown];
export function RemoteData<T>(url: string) {
return Resource((resource): Reactive<RemoteDataState<T>> => {
const result = cell("loading" as RemoteDataState<T>);
const controller = new AbortController();
resource.on.cleanup(() => controller.abort());
fetch(url, { signal: controller.signal })
.then((response) => response.json() as Promise<T>)
.then((data) => {
result.set(["data", data]);
})
.catch((error) => {
result.set(["error", error]);
});
return result;
});
}
import { use } from "@starbeam/react";
import { RemoteData } from "./remote-data.js";
export default function UserCard({ username }: { username: string }) {
const user = use(
() => RemoteData(`https://api.github.com/users/${username}`),
[username]
);
switch (user.type) {
case "loading":
return <div>Loading...</div>;
case "data":
return (
<div>
<img src={user.data.avatar_url} />
<h1>{user.data.name}</h1>
<p>{user.data.bio}</p>
</div>
);
case "error":
return <div>Error: {user.error.message}</div>;
}
}
<script lang="typescript">
import { use } from "@starbeam/svelte";
import { RemoteData } from "./remote-data.js";
export let username: string;
$: user = use(RemoteData(`https://api.github.com/users/${username}`));
</script>
{#if user.type === "loading"}
<div>Loading...</div>
{:else if user.type === "data"}
<div>
<img src={user.data.avatar_url} />
<h1>{user.data.name}</h1>
<p>{user.data.bio}</p>
</div>
{:else if user.type === "error"}
<div>Error: {user.error.message}</div>
{/if}
<script setup lang="ts">
import { use } from "@starbeam/vue";
import { RemoteData } from "./remote-data.js";
const props = defineProps<{ username: string }>();
const user = use(() => RemoteData(`https://api.github.com/users/${props.username}`));
defineExpose({ user });
</script>
<template>
<div v-if="user.type === 'loading'">Loading...</div>
<div v-if="user.type === 'data'">
<img :src="user.data.avatar_url" />
<h1>{user.data.name}</h1>
<p>{user.data.bio}</p>
</div>
<div v-if="user.type === 'error'">Error: {user.error.message}</div>
</template>
import { use } from "@starbeam/ember";
import { LiterCounter } from "#reactive/liter-counter";
export default class LiterLikeButton extends Component {
readonly liters = use(LiterCounter);
<template>
<button {{on "click" this.liters.increment}}>Add a liter</button>
<p>{this.liters.description}</p>
</template>
}