Mazzarolo Matteo
Mazzarolo Matteo

Just Tap: yet another small React-Native game

By Mazzarolo Matteo


Today I published on GitHub Just Tap: yet another small React-Native game I developed in my free time for testing a new stack.
Hooks! 🎣

logo

Overview

New React feature, new test project!
This time I wanted to test the shiny React-Hooks and achieve a fully type-safe Redux coverage.
I also thrown into the mix Redux-Saga for handling the game loop and Immer for handling the reducers’ immutability.

Features

  • Hooks in React-Native! 🎣
  • Works on both Android and iOS
  • Game timing and events handled by Redux-Saga
  • Type-safe Redux coverage thanks to Typesafe-Actions
  • Immer!

Libraries

  • React-Native & TypeScript
  • Redux
  • Immer
  • Redux-React-Hook
  • Redux-Saga
  • Typesafe-Actions

Known issues

The current version of React-Native (~0.57.0) has a rendering issue when using the useEffect hook that makes it delay the first component render.
I noticed that using a console.warn in the components that use the useEffect hook fixes the issue and I used it as a workaround while developing.
(Yes, this means that the app doesn’t run smoothly when built in release mode yet).

Here is a comparison of the app with and without the console.warn calls (check the delay while transitioning between the menu and the game board):

What I learned by building this project

React-Hooks

I’m still not sure if hooks live up to the hype but I like them so far…
In this project I tried using hooks in a few different ways and it was surprisingly fun!

  • They’re way more re-usable and composable than what I expected
  • You can finally use functional components without any drawback
  • The more I use them the more I feel like we should be careful about creating too many hooks. As a (bad) example, in this project I created 2/3 hooks that are not re-used nor contain any complex logic 👎
  • All the components of this app are pretty small, but I’d like to see how readable would be a complex component that uses hooks

Typescript, Redux & Immer

There’s not too much to say here, I liked typesafe-actions way more then expected (the docs are great and the maintainer is doing a really great job).
With typesafe-actions, immer, redux-react-hook, and a small custom action mapper I was been able to achieve a great Redux type coverage without all the classic Redux boilerplate.
I’m also glad I used redux-saga for handling the game events/timings because… almost the entire logic of the game is contained in a single saga which controls the game flow; the components just “react” to it:

export const runRoundSaga = function* () {
  yield delay(PREPARE_BOARD_DURATION);
  yield put(actions.play());
  const { isTimeLimitReached, isBoardClear } = yield race({
    isTimeLimitReached: call(runTimerSaga),
    isBoardClear: call(checkBoardClearSaga),
  });
  if (isTimeLimitReached) {
    const score = yield select(getScore);
    yield put(actions.endGame({ score }));
    yield delay(CLEANUP_BOARD_DURATION);
    yield put(actions.showResult());
  } else {
    yield delay(TILE_TAP_ANIM_DURATION * 2);
    yield put(actions.showInterlude());
    yield delay(INTERLUDE_DURATION);
    yield put(actions.startNewRound());
  }
};

Unfortunately I wasn’t able to make the sagas as much type-safe as I wanted, but I guess it’s related to how TypeScript handles the generators.
I also used // @ts-ignore in a couple of hooks because when I created them there was still no TypeScript support available for the hooks.