Beginning development with ReactJS & TypeScript (Part-1)

The official tutorial from React does a pretty good job of introducing React to a Beginner through building Tic-Tac-Toe. But it’s incomplete as it doesn’t put any focus on Unit Testing and provides no introduction of Typing through TypeScript or Flow, which can be very beneficial in the course of any non-trivial app development. The purpose of this post is to add TypeScript to the mix and also

  • Retain Livereload features that come with the out-of-the-box starter template through the ‘create-react-app’ Node module
  • Gain from Typing: Receive live feedback of Transpile time checks to ensure that typing contracts are met. This helps immensely with discovering bugs early on.
  • Introduce Unit Tests and focus on various ways components can be (and should be) tested.
  • Considerations for Components to properly document the typings of any newly introduced variables and method i.e the Component Contracts.

I highly recommend completing the official tutorial from React before continuing this tutorial.
The Source code for this tutorial can be found here. It’s based on Microsoft’s own starter template for TypeScript & React.

Setup

  • Clone the repo
  • git clone https://github.com/batchu/Tic-Tac-Toe.git
  • The Project is tagged at different stages starting with ‘step-0’. Let’s start with step-0 which includes the starter setup, basic components and the necessary configuration needed to begin our development.
  • git checkout step-0
  • From here (step-0) you can create your own custom branch to start making commits.
  • Git checkout -b some_branch_name
  • Alternatively If you’re using Intellij, Simply open the project, Click on Run -> Edit Configurations and Add a new item under ‘npm’. Fill in the location of ‘package.json’, select ‘run-script’ under ‘Command’ and type in ‘start’ within the ‘Scripts’ field. Save the configuration and it will now be available for use on the top-right side of the screen.
  • It’s a good practice to take advantage of Git’s features by frequently committing, branching as needed to help organize your progress. You can always diff with the nearest “tag” version if you’re stuck to fix any problems. (Example: Say you’re at step-3 and run into an issue with your implementation. You may diff your current state with the tag step-4 to see the differences)
  • I will refer to Components by their name within this tutorial.(For Example, I’ll refer to the “Board Component” simply as the Board)

Introduction to the Project

Since we’re going to build a simple version of Tic-Tac-Toe, we will start with the following Component hierarchy

  • App -> Game -> Board -> Square(s)

For now, let’s maintain these components within App.tsx. Rendering begins with the index.tsx which renders the App (Component). The Game (Component) doesn’t do anything interesting at this point other than loading the Board. But this will change in the future.

The Board renders Nine (9) Squares through each by calling the renderSquare() method.

The renderSquare() method passes the value to be displayed to the Square. Notice the type of input param to the renderSquare method. This helps us ensure that the right type of value is passed to the method.

class Board extends React.Component {

    renderSquare(i: number) {
        return (
            <Square value={i} />
        );
    }
    render() {
        return (
            <div>
                <div className="status">{status}</div>
                <div className="board-row">
                    {this.renderSquare(0)}
                    {this.renderSquare(1)}
                    {this.renderSquare(2)}
                </div>
                <div className="board-row">
                    {this.renderSquare(3)}
                    {this.renderSquare(4)}
                    {this.renderSquare(5)}
                </div>
                <div className="board-row">
                    {this.renderSquare(6)}
                    {this.renderSquare(7)}
                    {this.renderSquare(8)}
                </div>
            </div>
        );
    }
}

The Square grabs this value from Props and displays it. We declare that the Square adheres to SquareProps TypeScript Interface and an Empty State Map ({}) when extending React.Component.

class Square extends React.Component <SquareProps, {}> {
    render() {
        return (
           <button className="square">
               {this.props.value}
           </button>
        );
    }
}

Since the attribute ‘value’ within props has been created by us, we need to declare it for type checking purposes within SquareProps.

 interface SquareProps {
 value: number;
 }

Try running it in it’s current state (npm run start) and you should see a grid with 9 squares filled with numbers from 0 to 8.

Let’s attach a click Event to the button within a Square to invoke an anonymous function and simply alerts a message (checkout step-1 tag to arrive at this state).

class Square extends React.Component <SquareProps, {}> {
    render() {
        return (
           <button
                className="square"
                onClick={() => {alert(`clicked on ${this.props.value}`); }}
           >
               {this.props.value}
           </button>
        );
    }
}

Now let’s refactor the Board by adding a constructor to hold a state variable ‘squares’ which stores the values of all the squares as an Array. Each value is retrieved and passed on to the Square. An Event Handler function is also added to the Board and is sent to the Square through props. This way the Square can simply call this function for handling the click event. We are basically passing the values and the behavior to the Square from it’s parent Board.

class Board extends React.Component <BoardProps, {}> {

    constructor() {
        super()
        this.state = {
            squares: Array(9).fill(null)
        }
    }

    handleClick(i:number) {
       alert(`Handling click in the Board Cmp now for ${i}`)
    }

    renderSquare(i:number) {
        return (
            <Square
                value={`${i}`}
                onClick={()=> this.handleClick(i)}
            />
        );
    }

    render() {
        return (
            <div>
                <div className="status">{status}</div>
                <div className="board-row">
                    {this.renderSquare(0)}
                    {this.renderSquare(1)}
                    {this.renderSquare(2)}
                </div>
                <div className="board-row">
                    {this.renderSquare(3)}
                    {this.renderSquare(4)}
                    {this.renderSquare(5)}
                </div>
                <div className="board-row">
                    {this.renderSquare(6)}
                    {this.renderSquare(7)}
                    {this.renderSquare(8)}
                </div>
            </div>
        );
    }
}

The Square needs to be modified to simply invoke the click handler method it received through props.

class Square extends React.Component <SquareProps, {}> {
    render() {
        return (
           <button className="square">
               {this.props.value}
           </button>
        );
    }
}

Since the “onClick()” method is a custom method, it needs to be declared in the SquareProps interface.

interface SquareProps {
     value:string;
     onClick:()=>void
 }

Now, let’s try to change the value of a clicked Square and store it in the squares state variable within the Board. (git checkout step-3).

Here are the updated handleClick() method. Notice that the squares is assigned a copy of the original squares stored in the state by slicing it. This is to ensure that the original object within the state remains unmodified. Data immutability goes a long way in improving the design, efficiency and reducing bugs caused by unintentional modifications of objects. A consequence of Data Immutability is the simplification of Multi Threading implementations. This basically avoids race conditions as the original Data objects remain unmodified. Each thread can happily do it’s processing without the need of complex and expensive Thread Synchronization techniques.

handleClick(i:number) {
         const squares = this.state.squares.slice();
         squares[i] = 'X';
         this.setState({squares: squares})
     }

Here’s the updated renderSquare method. The value and the onClick() method are passed through props to the Square component.

renderSquare(i:number) {
         return (
              this.handleClick(i)}
             />
         );
     }

Let’s look at the changes made for the Square. The onClick handler simply invokes the onClick() method it received through props from it’s parent Board. Note that this new “onClick” method within it’s props needs to be declared explicitly within the SquareProps interface. So it is amended.

interface SquareProps {
    value:string;
    onClick:()=>void
}
class Square extends React.Component <SquareProps, {}> {
    render() {
        return (
            <button
                className="square"
                onClick={() => this.props.onClick()}
            >
                {this.props.value}
            </button>
        );
    }
}

Let’s continue with the above implementation by writing the necessary Unit tests in Part-2 of this tutorial.

One thought on “Beginning development with ReactJS & TypeScript (Part-1)

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.