Data Cells and Formula Cells

Data Cells

import { reactive } from "@starbeam/core";

const state = reactive({
  liters: 0,
});

const increment = () => {
  state.liters++;
};

function format(liters: number) {
  return new Intl.NumberFormat("en-US", {
    style: "unit",
    unit: "liter",
    unitDisplay: "long",
  }).format(liters);
}

format(state.liters); //? "0 liters"

increment();
format(state.liters); //? "1 liter"

increment();
format(state.liters); //? "2 liters"

Formula Cells

import { reactive, formula } from "@starbeam/core";

const description = formula(() => format(state.liters));

subscribe(description, (subscription) => {
  console.log(subscription.poll());
});

increment(); // ChangedValue(1)
increment(); // ChangedValue(2)

In UI Frameworks

Making it Universal: Turning it Into a Resource

import { reactive } from "@starbeam/core";
import { formula } from "@starbeam/reactive";

export const LiterCounter = resource(() => {
  const state = reactive({ liters: 0 });

  const increment = () => {
    state.liters++;
  };

  function format(liters: number) {
    return new Intl.NumberFormat("en-US", {
      style: "unit",
      unit: "liter",
      unitDisplay: "long",
    }).format(liters);
  }

  const description = formula(() => format(state.liters));

  return {
    description,
    increment,
  };
});

Using a Class

import { cell } from "@starbeam/reactive";

export class LiterCounter {
  #liters = cell(0);

  increment() {
    this.#liters.current++;
  }

  get description() {
    return this.#format(this.#liters.current);
  }

  #format(liters: number): string {
    return new Intl.NumberFormat("en-US", {
      style: "unit",
      unit: "liter",
      unitDisplay: "long",
    }).format(liters);
  }
}

React

import { LiterCounter } from "#reactive/liter-counter";
import { use } from "@starbeam/react";

export function LiterLikeButton() {
  const liters = use(LiterCounter);

  return (
    <>
      <button onClick={() => liters.increment()}>Add a liter</button>
      <p>{liters.description}</p>
    </>
  );
}

Svelte

<script>
  import { LiterCounter } from "#reactive/liter-counter";
  import { use } from "@starbeam/svelte";

  $: liters = use(LiterCounter);
</script>

<button on:click={liters.increment}>Add a liter</button>
<p>{liters.description}</p>

Vue

<script>
import { use } from "@starbeam/vue";
import { LiterCounter } from "#reactive/liter-counter";

export default {
  setup() {
    const { increment, description } = use(LiterCounter);

    return { increment, description };
  },
};
</script>

<template>
  <button v-on:click="increment">Add a liter</button>
  <p>{description}</p>
</template>

Ember

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>
}