Skip to content

Mythica: React with OvermindJS

Posted on:June 12, 2023 at 08:00 PM

image

This series of articles focuses on building a full-stack app with the following technology stack: PlanetScale - Prisma - tRPC - React. The project name is mythica and it will allow users to collect random mythical creatures. Here is the full list of the articles:

Table of contents

Open Table of contents

Overview

In this article we will be finalizing our Mythica series by adding the game logic with the aid of OvermindJS. Basically, we want the user to be able to hunt a creature by clicking on the heart icon, we will store user selections in our app state’s huntedCreatures array, which contains the ids of all selections made by user. Another piece of state is won attribute which is set to true if the user managed to hunt 5 unique creatures, the user keeps playing until they reach this number however if they pick the same creature in the same run, this creature is removed from the list.

1. Adding OvermindJS app state

In client/src directory add state.ts file with the following content.

import { IContext, createOvermind, derived } from "overmind";
import { StateHook, createActionsHook, createStateHook } from "overmind-react";

type State = {
  state: {
    huntedCreatures: Array<number>; // list of ids of hunted creatures
    won: boolean;
    len: number;
  };
};

export const useAppState = createStateHook() as StateHook<IContext<State>>;
export const useAction = createActionsHook();

export const appState = createOvermind({
  state: {
    huntedCreatures: [],
    len: 0,
    won: false,
  },
  actions: {
    huntCreature({ state }: State, creatureId: number) {
      console.log(creatureId);
      if (state.huntedCreatures.includes(creatureId)) {
        state.huntedCreatures = state.huntedCreatures.filter(
          id => id != creatureId
        );
        state.len--;
      } else {
        state.huntedCreatures.push(creatureId);
        state.len++;
      }
      if (state.len == 5) {
        state.won = true;
        state.huntedCreatures = [];
        state.len = 0;
      }
      console.log({ len: state.len });
      console.log(state.huntedCreatures);
    },
  },
});

State type contain different pieces of state in our app like so:

  1. huntedCreatures list of ids of the creatures the user hunted.
  2. won to determine if user has collected exactly an n number of unique creatures, which is hardcoded for now to 5.

2. Provide state to the entire app

Inside App.tsx we need to wrap the entire app with Provider from overmind and provide our appState to it like so

<Provider value={appState}>...</Provider>

3. GameResult Component

We have a very simple UI to view the result of the game by consuming our appState

import { useAppState } from "../state";

export const GameResult = () => {
  const { len, won } = useAppState(state => state);

  return (
    <>
      <p>{len}</p>
      <p>{won ? "You won" : ""}</p>
    </>
  );
};

4. Hunt Action

We want a creature to be hunted when the heart icon is pressed. So we execute our huntCreature action and then refetch a new creature.

<IconHeart
  onClick={() => {
    actions.huntCreature(creature.id);
    response.refetch();
  }}
  size="1.1rem"
  className={classes.like}
  stroke={1.5}
/>

And that is it we have finalized the game logic and it is working pretty nice now.

Conclusion

In this tutorial we learned how to use OvermindJS for state management.

You can find the final code for this article here