Tutorial: 🔢 Simple Counter

This tutorial directly corresponds to the "Hello World" tutorial of the Croquet Library. In fact the model side looks exactly the same. The following document assumes you are familiar with the main concepts presented there. The source code for this tutorial is available on GitHub.

The following example uses Vite for build. Other bundlers work fine also, but Vite is easy to get started as of writing in early 2024.

We start by creating a file main.jsx, and importing the required dependencies:

import ReactDOM from "react-dom/client";
import React from "react";
import {
  useReactModelRoot,
  CroquetRoot,
  ReactModel,
} from "@croquet/react";

We then define the CounterModel class. It has one attribute called "count" (this.count), and two methods: resetCounter and tick. When the object is being initialized, we set the initial counter value to zero, and schedule the tick function to be called within 1000 milliseconds. We also configure the model to call the resetCounter method whenever it receives a reset event within the this.id scope. It's a common practice to use this.id to indicate that this particular model's count has been changed or requested to be reset.

Whenever the counter is changed, either in the resetCounter or in the tick methods, it publishes a count event.

Note that after the tick function is called, it schedules its next call, just like when the object was initialized.

class CounterModel extends ReactModel {
  init(option) {
    super.init(option)
    this.resetCounter()

    this.subscribe(this.id, "reset", this.resetCounter);

    this.future(1000).tick()
  }

  resetCounter() {
    this.count = 0;
  }

  tick() {
    this.count += 1;
    this.future(1000).tick();
  }
}

After defining the model, we have to register it in the Croquet framework.

CounterModel.register("CounterModel");

Now, we define the CounterApp, which will be our top level React component. We use the InCroquetSession component to provide the running Croquet session to its children. This component takes the role of the Session.join method in @croquet/croquet, and can be configured with the same parameters. We recommend specifying those values in environment variables so that it's easier to manage them (e.g. switching between development and production keys). For more information about this topic, feel free to check out this article.

function CounterApp() {
  return (
    <CroquetRoot
      sessionParams={{
        apiKey: import.meta.env["VITE_CROQUET_API_KEY"],
        appId: import.meta.env["VITE_CROQUET_APP_ID"],
        password: "abc",
        name: "counter",
        model: CounterModel,
      }}
    >
      <CounterDisplay />
    </CroquetRoot>
  );
}

Next, we define the CounterDisplay component, which represents a view of the model defined above. For now, it only has the logic to render the live count of the replicated counter.

We use the useReactModelRoot hook to get hold of the CounterModel in our session, and display its count.

function CounterDisplay() {
  const model = useReactModelRoot();
  return (
    <div>
      {model.count}
    </div>
  );
}

To explore how we can have information flow back from our component to the CounterModel, let's set up our component to reset the counter when we click the count.

function CounterDisplay() {
  const model = useReactModelRoot();
  return (
    <div onClick={() => model.reset()}>
      {model.count}
    </div>
  );
}

Now, you should see a live ticking counter, which resets when you click it. If you open the same page in another tab or window, you should see the same live replicated counter as a second session participant.

To see a more interesting multi-user interaction, check out the next tutorial, where we implement a simple multiplayer music box.