Protectator

PixiJS v4.5 Application in a React Component

May 31, 2017 · 2 min read
PixiJS v4.5 Application in a React Component

I recently started a new project that uses both React and PixiJS. I had some trouble setting things up at the beginning, and had to do some searches on Google about how to correctly use a PixiJS renderer in a React Component.

While doing so, I discovered some inventive libraries like react-pixi that allows a PixiJS renderer to be controlled by React directly. It would probably be a good idea to use that lib, but I wanted to implement my own logic for handling PixiJS' renderer. I had some troubles trying to set things up in the "classic" and recommended PixiJS way. Other solutions I found are no longer working in the recent versions of PixiJS.

As no resource seemed to provide a simple explanation on how to correctly use PixiJS in a React Component now, I thought this post might be useful to some.

Techs used

Before going further, here is a list of which techs I'm using for this. I of course can't guarantee that this will work with different technologies or even different versions :

  • PixiJS 4.5.1
  • React 15.4.2
  • Typescript 2.1.0

With this I'm also using other libs like Flux or React-Router but they're not relevant as this post focuses purely on getting a PixiJS renderer to work in a React Component, not the general architecture of the Application.

The classic PixiJS way

On the examples visible on the website, the main class used to instantiate and control the renderer is to use PIXI.Application. The example code that uses PIXI.Application is as follows :

// Create the application
const app = new PIXI.Application();

// Add the view to the DOM
document.getElementById('whatever').appendChild(app.view);

// ex, add display objects
app.stage.addChild(PIXI.Sprite.fromImage('something.png'));

By trying to mimic this when implementing a Component, we would run into an issue. We're trying to add an element to the DOM tree directly, which isn't what React recommends (and we'd need ReactDOM).


A type-checked React-like way

There's no magic trick here, we'll still need to add the view "manually" to control it. But instead of referencing the DOM tree, we can use a feature from React called Refs to do things right. Here's what the documentation tells us :

React supports a special attribute that you can attach to any component. The ref attribute takes a callback function, and the callback will be executed immediately after the component is mounted or unmounted.

We'll use that to our advantage here and create a <div> that contains a ref attribute. We'll then be able to interact with it by using that reference instead of trying to access the DOM.

Instead of trying to explain much longer, here's the code in TSX of the Component whose goal is to manage and display an instance of a Pixi.Application :

PixiComponent.tsx

import * as React from 'react';
import * as Pixi from 'pixi.js';

interface IMainProps {}
interface IMainState {}
export class PixiComponent extends React.Component<IMainProps, IMainState> {
  app: Pixi.Application;
  gameCanvas: HTMLDivElement;

  constructor() {
    super();
  }

  /**
   * After mounting, add the Pixi Renderer to the div and start the Application.
   */
  componentDidMount() {
    this.app = new Pixi.Application(window.innerWidth, window.innerHeight);
    this.gameCanvas.appendChild(this.app.view);
    this.app.start();
  }

  /**
   * Stop the Application when unmounting.
   */
  componentWillUnmount() {
    this.app.stop();
  }

  /**
   * Simply render the div that will contain the Pixi Renderer.
   */
  render() {
    let component = this;
    return (
      <div ref={(thisDiv) => {component.gameCanvas = thisDiv}} />
    );
  }
}

Note that even though we're calling appendChild ourselves, we still didn't even use ReactDOM here. Everything is self-contained and correctly type-checked.As the component mounts, the ref attribute immediately calls our anonymous function which sets the gameCanvas to that element. The componentDidMount() function will then fire and reference it directly to add the Pixi.Application view (which is a Pixi.SystemRenderer).

You now have all possibility to control the Application and Renderer however you like. Want the state of the Game to persist when the Component mounts/unmounts ? Just save the main Pixi.Container in componentWillUnmount and add it back in componentDidMount.

Conclusion

I use that method in my own code and it works like a charm. Even though using ReactDOM and an id attribute would be possible, this is prone to errors. I like to keep things organized and I think the more things the compiler is able to check, the better. It also feels less like a hack and more like a reusable and self-contained Component.

Category: Development

Comments