Published:

This guide is designed to cover just the basics of React. There are at least a million other guides out there, but how many of them have an awesome spaceship control system example to work through?

The following is a basic introduction to React designed for people who have no experience with it before. To get started you will need a fairly decent understanding of Javascript and some knowledge of ES6 classes and module syntax will be useful. Whilst this tutorial won't cover every aspect of React, it will give you a good base from which to start making apps.

Contents

 


What is React?

This excerpt is taken from Wikipedia: "React allows developers to create large web-applications that use data and can change over time without reloading the page. It aims primarily to provide speed, simplicity, and scalability. React processes only user interfaces in applications.".

React is a Javascript library that allows an app to be broken up into individual components. These components live in a tree-like format where there is a base component and that in turn has child components nested inside it, just like in regular HTML where there is a body then perhaps a header and then nested within that a navigation and so on. 

These components can have their own data and content or receive it from their parents, in turn passing it down the chain. In this way React uses what is known as a one-way data flow (as opposed to a framework such as Angular which uses two-way data binding) where information is passed down from parent to child component.

React uses a virtual dom to create components known as JSX. It is essentially the same as HTML but stored within the JS and with a couple of slight differences in the way it is written. React uses the JSX you write and turns it into actual HTML in the DOM. Using the virtual DOM to store and work out changes to the HTML is much faster than writing out the real DOM over and over and it is mostly due to this that React gains its speediness.

React is known as an isomorphic language, meaning that it can be run in the browser, on a web server or natively on Android and IOS. This makes it a really powerful addition to any developer's toolbox as you can use the same language to accomplish many things. There are React versions of many other popular libraries and frameworks which give you easy integration with things like D3.js or three.js just to name a couple. 

Getting React setup

This React tutorial won't go into too much detail about how to get up and running with React. There are many ways to use it, most use some sort of bundler to combine all the component files into one JS file such as Webpack or Rollup.

React-create-app

This is a really easy way to get started, you just need to follow the instructions found here:

https://github.com/facebookincubator/create-react-app

React through Gulp with Sass and Pug

If you prefer to use Gulp and are a fan of Sass, my personal framework is an example of this:

https://github.com/alexplummer/framework-react

Building an app

In this tutorial we will be building a control system for a fleet of spaceships. This lends itself fairly well to React as there is a chain of command in the fleet which matches the one-way data flow found in React. Without further hesitation let's get started!

Adding some basic JSX

As mentioned before, React uses JSX to render out real HTML. This is really similar to regular HTML, so as long as you know that you basically know JSX. It is written within the Javascript rather than inside a regular HTML page and it is then parsed by React before being rendered to the real DOM.

 

Here is our first example of JSX:

import React from 'react';
import ReactDOM from 'react-dom';
  
ReactDOM.render(
    <h1>All systems online.</h1>, 
    document.querySelector('#app')
); 

 

The above code is basically asking React to render out some HTML. We first import React then ReactDOM which does the actual rendering to HTML. The ReactDOM.render method takes two parameters, the first is a block of JSX and the second is a DOM selector used to append the code to in your HTML. 

 

Here it is in Plunker, we can see that it simply renders out the H1 to the DOM:

 

This isn't doing a huge amount though, what we really need is a new component for our first and most important spaceship - the Mothership. The Mothership will be the base control ship issuing out commands to lesser ships within the fleet. It's good to keep each component in a separate file so things are organised neatly, so let's create a new file and import it using ES6 module syntax.

Creating a new component

Components can be written in a couple of ways, the simplest is to write it inside a function and pass it to the render method.

 

Let's create a new component for the Mothership with the following code:

import React from 'react';

function Mothership() {
  
  return (
    <div>
        <div className="Mothership">
            <h1>Mothership</h1>
            <p className="description">The main control ship in the fleet.</p>
            <p><strong>Control systems:</strong><span>Active</span></p>
        </div>
    </div>
  );
};

export default Mothership;

 

Notice here we don't have to import ReactDOM as we aren't actually rendering anything in this file, we only need React. Also note that the return value, our JSX, is wrapped in parenthesis. This stops an end of statement from occurring before the end of the multiline JSX block. Another variation between JSX and HTML can be seen here, as 'class' is a reserved word in JS, JSX uses 'className' instead. Apart from that, everything in the above example is the same as HTML. In case you are wondering, the extra classless div wrapping everything is used for styling purposes later on, however, aside from this make sure your component is always wrapped with some sort of parent tag such as a div.

 

Now we just import the Mothership component within app.jsx as though it was a regular HTML tag:

import React from 'react';
import ReactDOM from 'react-dom';
import Mothership from './Mothership.jsx';
  
ReactDOM.render(
    <Mothership />, 
    document.querySelector('#app') 
);

 

Notice the capitalised first letter of Mothership here. Capitalized tags indicate that the JSX tag is referring to a React component, lowercase means regular HTML.

 

Awesome, now we have a Mothership component, ready to issue out orders to other ships:

 

The CSS has already been setup to run with the rendered JSX, we can see the HTML working nicely inside the Mothership component corresponding to the JSX on the left.

Adding another ship

The Mothership is pretty weak by itself so let's call in a Battleship to add some firepower. The Mothership will be sending orders down to the Battleship, we can create a new component for the Battleship and pass these orders down to it using React's one-way data flow.

 

To start with we will use static markup, then add the data after:

import React from 'react';

class Battleship extends React.Component {

    render() {
        return (
            <div>
                <div className="Battleship">
                    <h2>Battleship</h2>
                    <p className="description">A first class war ship.</p>
                    <p><strong>Orders:</strong> <span>(orders will display here)</span></p>
                </div>
            </div>
        );
    }
}

// Exports
export default Battleship;

 

Right off the bat you can spot some differences with the previous component syntax, this time round we have used the ES6 class version of a React component which is what is normally used for most components. Some of the things to note include:

  • The class needs to extend React.component
  • There is a render method which returns the JSX for the component

 

We can now import the Battleship into the Mothership component, let's change it to use a class as well:

import React from 'react';
import Battleship from './Battleship';

class Mothership extends React.Component {

    render() {
        return (
            <div>
                <div className="Mothership">
                    <h1>Mothership</h1>
                    <p className="description">The main control ship.</p>
                    <p><strong>Control systems:</strong><span>Active</span></p>
                </div>
                <Battleship />
            </div>
        );
    }
}

// Exports
export default Mothership;

 

Great, we have two ships in our fleet although all the content is static and there are no orders being passed down from the Mothership to the Battleship. The next step is to get these orders being dynamically passed through the fleet using React's one-way data flow.

Passing down data

In order to get the data flowing, React uses props (properties) which are added to the component as attributes when it is called. These can then be referred to by the child component. Props can only be passed from parent component to child component hence the one-directional flow of data.

 

Let's add a prop to the Mothership so it can pass down an order to the Battleship:

import React from 'react';
import Battleship from './Battleship';

class Mothership extends React.Component {

    render() {
        return (
            <div>
                <div className="Mothership">
                    <h1>Mothership</h1>
                    <p className="description">The main control ship.</p>
                    <p><strong>Control systems:</strong><span>Active</span></p>
                </div>
                <Battleship orders="standby" />
            </div>
        );
    }
}

// Exports
export default Mothership;

 

In this case we are just passing a string down as a prop, 'standby' and assigning it to the prop 'orders', however as we will see you can also pass more complicated props such as objects and functions.

 

Now we need to adjust the Battleship component to pick up this prop:

import React from 'react';

class Battleship extends React.Component {

    render() {
        return (
            <div>
                <div className="Battleship">
                    <h2>Battleship</h2>
                    <p className="description">A first class war ship.</p>
                    <p><strong>Orders:</strong> <span>{this.props.orders}</span></p>
                </div>
            </div>
        );
    }
}

// Exports
export default Battleship;

 

Notice that the call to props is wrapped in curly brackets, anything in the JSX which is wrapped with these will be interpreted as a Javascript expression. We call the props within the expression using 'this.props' followed by the attribute name on the parent component used to pass the data.

 

Adding all this to a Plunker, we can see that the new ship is up and running:

 

This takes care of passing down some basic orders, but sometimes a component needs to store its own data. In our case the Battleship takes time to boot up its defences, so we will need to add some data to track the state of them.

Setting up a component's state

React uses state to control data within individual components. State is private to the component and it can only be accessed by passing it down to a child component via props. State default values are initially added to a constructor method inside the component's class, it can then be accessed in a similar way to props using 'this.state'.

 

Let's add some state to Battleship to track the status of its defences:

import React from 'react';

class Battleship extends React.Component {

    constructor(props) {
        super(props);

        this.state = {
            battleshipSystems: {
                defences: "coming online..."
            }
        }
    }

    render() {
        return (
            <div>
                <div className="Battleship">
                    <h2>Battleship</h2>
                    <p className="description">A first class war ship.</p>
                    <p><strong>Defences:</strong> <span>{this.state.battleshipSystems.defences}</span></p>
                    <p><strong>Orders:</strong> <span>{this.props.orders}</span></p>
                </div>
            </div>
        );
    }
}

// Exports
export default Battleship;

 

Now we have added a constructor to the class, passing one argument of 'props', which is then passed to its super. Then within the constructor we can create the initial state using 'this.state', in this case we add an object as the state so we can add to and modify it later on. You can then see the state being called with 'this.state.battleshipSystems.defences'.

Looking back at the 'orders' prop passed to the Battleship in the Mothership component, at the moment it is just a string. To allow for future additions like further orders this will have to be changed over to an object which can take a variety of state types. 

 

Adding a constructor with state to the Mothership:

import Battleship from './Battleship';

class Mothership extends React.Component {

    constructor(props) {
        super(props);

        this.state = {
            motherShipSystems: {
                controlSystems: "online"
            },
            fleetOrders: {
                battleship: "standby",
            }
        }
    }

    render() {
        return (
            <div>
                <div className="Mothership">
                    <h1>Mothership</h1>
                    <p className="description">The main control ship.</p>
                    <p><strong>Control systems:</strong><span>{this.state.motherShipSystems.controlSystems}</span></p>
                </div>
                <Battleship orders={this.state.fleetOrders} />
            </div>
        );
    }
}

// Exports
export default Mothership;

 

This is a good start but obviously in most apps you won't be working with static data, you will need to update it over time, we can begin to do that next

React lifecycle

The defences of the Battleship are coming online, for them to be fully operational takes a few seconds, we can add in a timeout to change the status to 'ready' 3 seconds after the ship is rendered. React has a set of lifecycle hooks which can be used to modify a component at specific times around when it is mounted (rendered) or when it receives props. 

Some of the lifecycle hooks include:

  • componentWillMount() - invoked immediately before rendering occurs
  • componentDidMount() - invoked immediately after rendering occurs
  • componentWillReceiveProps() - is invoked before a rendered component receives new props

In the analogy of our Battleship, we want the defences brought online after the ship has been deployed to the fleet. In React terms this corresponds to the 'componentDidMount()' hook. We can now call this method within our class and add a 'setTimeout' within it to update the status of the defence systems.

 

Let's update our Battleship class with the lifecycle hook:

import React from 'react';

class Battleship extends React.Component {

    constructor(props) {
        super(props);

        this.state = {
            battleshipSystems: {
                defences: "coming online..."
            }
        }
    }

    componentDidMount() {

        setTimeout(() => {
            this.setState({
                battleshipSystems: {
                    defences: "ready"
                }
            });
        }, 3000);
    }

    render() {
        return (
            <div>
                <div className="Battleship">
                    <h2>Battleship</h2>
                    <p className="description">A first class war ship.</p>
                    <p><strong>Defences:</strong> <span>{this.state.battleshipSystems.defences}</span></p>
                    <p><strong>Orders:</strong> <span>{this.props.orders}</span></p>
                </div>
            </div>
        );
    }
}

// Exports
export default Battleship;

 

Great, now after 3 seconds the defences are set to 'ready'. This is done by calling 'this.setState()', another React method; 'this.state' is only used to initialise state in the constructor. Anytime you want to update state remember to use 'this.setState()' with 'this' being the base context of the class.

 

We can add this to the Plunker to see it in action:

 

Now we are getting somewhere, but to wrap things up we need to add some more support ships to our fleet, right now we pretty light in terms of recon. Our recon ships are going to be setting and sharing updates on the status of the fleet so we will need to add in a way to pass state around components.

Sharing and passing state

So far we have only controlled the state directly within a component and passed it down the chain of command to children components. What happens when you need to share state amongst components or pass it back up to a parent? As React components can't access their parent directly (due to one-way data flow), the parent needs to provide a way of tracking its child components data. To do this we can pass a function as a prop and use it as a callback to receive data from the child component. Don't worry if this isn't clear we will run through the process shortly. 

We are now going to add in three new support ships to our fleet:

  • Carrier - deploys and controls smaller ships including the Scout and Scanning Vessel
  • Scout - a small visual scouting ship
  • Scanning Vessel - scans deep into space using a variety of sensors

The Carrier reports directly to the Battleship, orders are passed down the chain from Mothership to Battleship to Carrier which then controls the Scout and Scanning Vessel.

The Carrier, Scout and Scanning Vessel need access to the overall status of the fleet so they can respond to any threats.

 

Let's create a new file for the Carrier:

import React from 'react';
import Scout from './Scout';
import ScanningVessel from './ScanningVessel';

class Carrier extends React.Component {

    constructor(props) {
        super(props);

        this.state = {
            fleetReports: {
                activity: "no reports"
            }
        }
        this.alertFleet = this.alertFleet.bind(this);
    }

    alertFleet(message) {

        this.setState({
            fleetReports: {
                activity: message
            }
        });
    }

    render() {

        return (
            <div>
                <div className="Carrier">
                    <h3>Carrier</h3>
                    <p className="description">Controls smaller ships.</p>
                    <p><strong>Fleet reports:</strong> <span>{this.state.fleetReports.activity}</span></p>
                    <p><strong>Orders:</strong> <span>{this.props.orders.carrier}</span></p>
                </div>
                <Scout sendAlert={this.alertFleet} fleetReports={this.state.fleetReports.activity} />
                <ScanningVessel sendAlert={this.alertFleet} fleetReports={this.state.fleetReports.activity} />
            </div>
        );
    }
}

// Exports
export default Carrier;

 

Notice that there is an 'alertFleet()' callback in there which is then passed as a prop to Scout and ScanningVessel components. Make sure to bind the context of 'this' in the constructor function for any methods you create, in this case it is written as 'this.alertFleet = this.alertFleet.bind(this)'. That allows you to use 'this.setState' from within any function but keep the same base context of the class, which is where the React methods you extend from 'React.Component' also sit.

There are calls to the Scout and Scanning Vessel components as well, both of which need a way to send out unique alerts to the rest of the fleet. To handle these fleet reports we will need to use a form with a text input and some way to handle the submit action, both of which are easy to manage in React.

 

Here is our new Scout file:

import React from 'react';

class Scout extends React.Component {

    constructor(props) {
        super(props);

        this.state = {
            scoutReport: {
                activity: ""
            }
        }
        this.updateReport = this.updateReport.bind(this);
    }

    updateReport(e) {
        this.setState({
            scoutReport: {
                activity: e.target.value
            }
        });
    }

    reportToCarrier(e) {
        this.props.sendAlert(this.state.scoutReport.activity);
        e.preventDefault();
    }

    render() {
        return (
            <div className="Scout">
                <h4>Scout</h4>
                <p className="description">A small ship used as a lookout.</p>
                <p><strong>Scout report:</strong> <span>{this.state.scoutReport.activity}</span></p>
                <p><strong>Fleet report:</strong> <span>{this.props.fleetReports}</span></p>

                <form onSubmit={this.reportToCarrier}>
                    <label>Send alert</label>
                    <input 
                        type="text" 
                        value={this.state.scoutReport.activity} 
                        onChange={this.updateReport} 
                        placeholder="Enter a report"
                    />
                    <input type="submit" />
                </form>
            </div>
        );
    }
}

// Exports
export default Scout;

 

Plus here is our new ScanningVessel file, it's fairly similar:

import React from 'react';

class ScanningVessel extends React.Component {

    constructor(props) {
        super(props);

        this.state = {
            scanResults: {
                activity: ""
            }
        }
        this.updateReport = this.updateReport.bind(this);
        this.reportToCarrier = this.reportToCarrier.bind(this);
    }

    updateReport(e) {
        this.setState({
            scanResults: {
                activity: e.target.value
            }
        });
    }

    reportToCarrier(e) {
        this.props.sendAlert(this.state.scanResults.activity);
        e.preventDefault();
    }

    render() {
        return (
            <div className="ScanningVessel">
                <h4>Scanning Vessel</h4>
                <p className="description">Performs scans deep into space.</p>
                <p><strong>Scan results:</strong> <span>{this.state.scanResults.activity}</span></p>
                <p><strong>Fleet report:</strong> <span>{this.props.fleetReports}</span></p>

                <form onSubmit={this.reportToCarrier}>
                    <label>Send alert</label>
                    <input 
                        type="text" 
                        value={this.state.scanResults.activity} 
                        onChange={this.updateReport} 
                        placeholder="Enter a report"
                    />
                    <input type="submit" />
                </form>
            </div>
        );
    }
}

// Exports
export default ScanningVessel;

 

Forms in React

With these two additional components we now have a way of adding dynamic content to the app through the form. Without going into too much detail about forms in React, in the above example we are calling two event handlers:

  • onChange - called when the element is modified
  • onSubmit - called when the form is submitted

We have set the 'onChange' handler on the text input so that it calls 'updateReport()' any time the input is changed. This then sets the state of the component by referencing the target event with 'e.target.value'.

When the submit input is clicked, the 'onSubmit' handler fires calling the 'reportToCarrier()' method which passes the state back up to the callback function. This in turn calls 'alertFleet()' within the Carrier component which then passes the new state down to other ships through props. 

Now we have a working scouting system where both of the recon ships can alert the rest of the fleet with a common state passed back up to the Carrier. It is worth noting that when you need to share state between components, find the nearest common ancestor and set the callback function there. In our case the nearest ancestor to Scout and ScanningVessel happens to be just one level up.

 

We can add a final Plunker with everything up and running:

 

Conclusion

Well done for making it this far, that was a lot to take in! Hopefully this will have covered most of the major themes with React, definitely go on to check out the React website for its tutorials to fill in any gaps and learn some further React methods. 

 

Here is a visual reference of where we reached with our spaceship fleet control system:

An overview of the battleship control system

 

As well as the Plunker you can view the final system here:

http://alexplummer.com/sites/react-starship/

I have also uploaded the project here using my personal framework: 

https://github.com/alexplummer/starship-control-system

This concludes the guide, stay tuned for more space-themed analogies in the next tutorial which will cover Redux with React.



Comments