Phaser game with a React UI

In this video, we show developing a game with Phaser and using React for the user interface. Using React to alleviate the burden of handling the UI in Canvas. RequestAnimationFrame can be expensive and should be used for the game only. The canvas game shouldn’t be wasting valuable updates for the UI.

The game we’re developing is using Next.js (a React framework) and Phaser (javascript game engine). Next.js compiles a static html version of the game that can be used iOS/Android webview for mobile, NW.js/Electron for desktop (similar to what Discord does).

The game is a NextJS application that from there, it builds the Phaser game and game starts up normally. However, the Nextjs application is handling the UI elements like in the video: merchant dialog, player bag, player armory, and other dialogs like the death dialog.

This is a similar approach to older console games that used Scaleform - which under the hood was using Flash and overlaying the UI created by a Flash developer. From there, Scaleform would sit on top of the c++ game engine.

Writing the bridge between Canvas and React

For the bridge to work between Phaser and React, use an event listener:

  • addEventListener on the document object to send objects back and forth.
  • The implementation of addEventListener is contained within a hook that implements an Effect Hook: useEffect so that state can be passed in.

The performance issue that was solved here was to convert the class components (Bag, Armory, DeathDialog, MerchantDialog) to functional components and then use hooks to manage the add/remove listeners.

Types of Hooks

  • State Hooks: useState - called inside a function component to add some local state. (example: a counter)
  • Effect Hooks: useEffect - perform side effects from a function component. (example: listening to an event fire from the DOM)
  • Context Hooks: useContext - subscribe to React context without introducing nesting. (example: Dark & Light themes)
  • Reducer Hooks: useReducer - And useReducer lets you manage local state of complex components with a reducer. (dealing with complex state logic)

Rules of using Hooks

Take the time to learn hooks and using functional components.

  • Call Hooks at top level only: Only call Hooks at the top level. Don’t call Hooks inside loops, conditions, or nested functions.
  • Functional Components only: Only call Hooks from React function components. Don’t call Hooks from regular JavaScript functions. (See Custom Hooks.)

If you are using class components, do not worry - porting class components to functional components is trivial. Simply follow the pattern of functional components when porting classes.

Pay close attention to useState when rewriting your new functional components. Once you spend time writing functional components, we find you’ll enjoy using them and paired with hooks makes the component design simple to implement and iterate on the design of the interface.

A Simple Example

Below is a bare bones example of a using a Hook and implementing useEffect to attach events with addEventListener. Use this example to experiment with different types of hooks.

Have a solid understanding of Hooks

Taking the time to understand hooks and why we are using them is the first important building block to building the bridge between the game engine and UI. Pick the areas that you need side effects - the Effect Hook, which allows for data fetching, DOM updates, and for our example for the bridge: we use addEventListener within useEffect. In the callback for useEffect, pass in removeEventListener to clean up that instance of the event listener.

Resources and frameworks

Here’s all the resources and frameworks used in the video: