Learn React function components by building a Checkers board

React components are like puzzle pieces.

If you have been somewhat active in a development community, you probably have already heard about React. I stepped aside of it for quite a long time, but now I don’t have any alternative: I need to mingle with React code at work.

In this tutorial, I will be showing you how to use its recommended basic building blocks: the function components. I will do so by building a simple Checkers board.

Here’s a quick summary for what you’ll learn :

  • The JSX syntax, which resembles to HTML with subtle differences.
  • How to reuse and parameterize our components through component properties, so we can avoid duplication in our code.
  • How to pass data from higher level components to lower level components, by using a top-down data flow.
  • How to create nested components, so we can compose our views with a greater flexibility.

Are you ready to dive in?

Project Setup

Let’s start with a real quick setup.

I advise you to use the create-react-app1 package that greatly simplifies the setup of any React application. The only prerequisite is to have a recent version of Node.js2 installed on your development environment, which I won’t cover in this tutorial (but the official documentation explains it very well).

Then you will have to use the following commands in your terminal (from the folder where you want to store your app files):

npx create-react-app react-Checkers-board
cd react-Checkers-board
npm install
npm start

Both npx and npm commands come with Node.js default packages. The start script is created by create-react-app and will run your project at the address localhost:3000.

If you keep your browser open during development, you will see that the changes you made in the project files will automatically trigger a refresh of the page. This is the hot reload mode from webpack-dev-server that has been automatically installed with the command create-react-app.

You don’t have to configure anything more. So we’re ready to jump into development.

Components are not HTML

If you open the src/App.js file, you will notice the following code:

function App() {
  return (
    <div className="App">
      <header className="App-header">
        <img src={logo} className="App-logo" alt="logo" />
        <p>
          Edit <code>src/App.js</code> and save to reload.
        </p>
        <a
          className="App-link"
          href="https://reactjs.org"
          target="_blank"
          rel="noopener noreferrer"
        >
          Learn React
        </a>
      </header>
    </div>
  );
}

export default App

This markup language may seem familiar, but you have probably noticed something unusual: that className attributes is not part of the valid HTML syntax. Indeed, React components are written in React’s own markup language: JSX.

You can think of it as a javascript templating engine for HTML. All native javascript functions and API will be available, but the HTML part has to be compiled by React in order to be rendered by the browser. This is part of the tooling that create-react-app has automatically installed and configured for us.

If you step up in the src/index.js you will see the following code:

import `App` from './App'

ReactDOM.render(
  <React.StrictMode>
    <App />
  </React.StrictMode>,
  document.getElementById('root')
);

This file is the entry point of our application, this is where React components are inserted into the DOM.

The <App /> component will be replaced by the JSX markup we have seen defined in the src/App.js file.

The whole JSX markup passed to the ReactDOM.render() method will be appended to the element with the id root in our page.

Let’s modify this component to create our Checkers board:

function App() {
  return (
    <table className='no-border'>
      <thead>
        <tr><th></th><th>a</th><th>b</th><th>c</th><th>d</th><th>e</th><th>f</th><th>g</th><th>h</th><th></th></tr>
      </thead>
      <tbody>
        <tr><th>8</th><td className='square light'></td><td className='square dark'></td><td className='square light'></td><td className='square dark'></td><td className='square light'></td><td className='square dark'></td><td className='square light'></td><td className='square dark'></td><th>8</th></tr>
        <tr><th>7</th><td className='square dark'></td><td className='square light'> /* ... */</tr>
        /* ... */
      </tbody>
      <tfoot>
        <tr><th></th><th>a</th><th>b</th><th>c</th><th>d</th><th>e</th><th>f</th><th>g</th><th>h</th><th></th></tr>
      </tfoot>
    </table>
  );
}

And add the needed styling in src/App.css:

.no-border {
  border-collapse: collapse;
}

.square {
  width: 83px;
  height: 83px;
}

.light {
  background-color: #FFCE9E;
}

.dark {
  background-color: #D18B47;
}

You might appreciate or not the use of a <table> element to represent our board, but you will certainly agree that the actual code contains a lot of repetition, which makes it harder to read and to modify. Let’s change this.

Components are reusable

From the previous code, we can identify two types of repeated elements: the rows (tr) and the cells (td): this will make good candidates to be transformed into components.

Let’s create a component for our rows first:

function Row(props) {
    const isEven = props.number % 2 === 0

    return (
        <tr>
            <th>{props.number}</th>
            {isEven ? <td className='square light'></td> : null}
            <td className='square dark'></td>
            <td className='square light'></td>
            <td className='square dark'></td>
            <td className='square light'></td>
            <td className='square dark'></td>
            <td className='square light'></td>
            <td className='square dark'></td>
            {isEven ? null : <td className='square light'></td>}
            <th>{props.number}</th>
        </tr>
    )
}

export default Row

We need to declare the function component that returns the JSX markup corresponding to one row on our Checkers board.

There are two ways one row differs from another:

  • Each row has an associated number.
  • Even rows start by a light square, and odd rows start by a dark square.

Have you noticed the props argument that is passed to our function component? This behavior of React let us pass values from high level components to lower level components.

This is how it looks like from the App component:

import Row from './components/row'

function App() {
  return (
    <table className='no-border'>
      <thead>
        <tr><th></th><th>a</th><th>b</th><th>c</th><th>d</th><th>e</th><th>f</th><th>g</th><th>h</th><th></th></tr>
      </thead>
      <tbody>
        {[8, 7, 6, 5, 4, 3, 2, 1].map(number => <Row key={number.toString()} number={number} />)}
      </tbody>
      <tfoot>
        <tr><th></th><th>a</th><th>b</th><th>c</th><th>d</th><th>e</th><th>f</th><th>g</th><th>h</th><th></th></tr>
      </tfoot>
    </table>
  );
}

But you can notice that I added two properties to this component:

  • key is a default attribute required by React when several components are inserted through an iteration, it needs to be different for each element in the application.
  • number is a custom attribute. React will extract the value of each attribute that is not a default one, and map these values as members of the props parameter that is passed to the component function.

This behavior of React components let us write the markup for each component only once, and then compute them with different properties each time we need to add a new one.

Note: Because JSX markup can only compute expressions inside its {}, we couldn’t use a for loop instead of the Array.prototype.map() call.

Exercise 1: Before we continue this tutorial, could you extract the Square component (a singular cell on the Checkers board), just as we did with the row component?

You can find the source code on GitHub. As well as my solution

Data flows top-down through components

Now that we create those Row and Square components dynamically, how would we place our Checkers pieces inside specific squares on the board?

The only solution would be to store that data in the top level component: the App itself. We will use a two-dimensional array, where we’ll store the pieces as o letters.

The white player pieces will be in upper case, and the black player pieces in lower case. The empty squares will be represented by spaces.

function App() {
  const data = [
    [' ', 'o', ' ', 'o', ' ', 'o', ' ', 'o'],
    ['o', ' ', 'o', ' ', 'o', ' ', 'o', ' '],
    [' ', 'o', ' ', 'o', ' ', 'o', ' ', 'o'],
    [' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '],
    [' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '],
    ['O', ' ', 'O', ' ', 'O', ' ', 'O', ' '],
    [' ', 'O', ' ', 'O', ' ', 'O', ' ', 'O'],
    ['O', ' ', 'O', ' ', 'O', ' ', 'O', ' ']
  ]

  return (
    <table className='no-border'>
      <thead>
        <tr><th></th><th>a</th><th>b</th><th>c</th><th>d</th><th>e</th><th>f</th><th>g</th><th>h</th><th></th></tr>
      </thead>
      <tbody>
        {data.map((rowData, index) => {
          const number = data.length - index
          
          return <Row key={number.toString()} number={number} data={rowData} />
        })}
      </tbody>
      <tfoot>
        <tr><th></th><th>a</th><th>b</th><th>c</th><th>d</th><th>e</th><th>f</th><th>g</th><th>h</th><th></th></tr>
      </tfoot>
    </table>
  );
}

You can see how we have changed the data we iterate over. Now that each row contains actual data, we need to compute the rows numbers from the index parameter that is passed to javascript’s native Array.prototype.map() function.

Then each row has to split the data received, so we can iterate a second time to generate the squares:

function Row(props) {
    return (
        <tr>
            <th>{props.number}</th>
            {props.data.map((squareData, index) => {
                const column = String.fromCharCode(97 + index)

                return <Square key={column + props.number} row={props.number column={column} data={squareData} />
            })}
            <th>{props.number}</th>
        </tr>
    )
}

The code is a little similar here, except that the index is a letter, computed from an UTF-16 code unit. 3

Now we need to use this data in order to display the Checkers pieces images. Since Checkers basic pieces (called ‘men’) are simple round tokens, we will use simple svg4 files to represent them. Here’s the code for a black man:

<svg 
    width="64px" 
    height="64px" 
    viewBox="-32 -32 64 64" 
    xmlns="http://www.w3.org/2000/svg" 
    xmlns:xlink="http://www.w3.org/1999/xlink">
        <title>Checkers black man</title>
        <circle id="man" cx="0" cy="0" r="32" stroke="none" fill="#000" />
</svg>

For white men, we just need to change the fill attribute of the <circle> element to #fff (and the <title> element would be nice to change too).

So our Piece component could be as simple as the following:

import blackMan from '../assets/black-man.svg'
import whiteMan from '../assets/white-man.svg'

function Piece(props) {
    const player = props.data === 'O' ? 'white' : 'black'

    return (
        <img src={player === 'white' ? whiteMan : blackMan} alt={`A ${player} man.`} className={'piece'} />
    )
}

As you can see, we start by importing the svg images inside our component definition. This trick will compute the correct path for our images when our code will be processed by the scripts that create-react-app has installed for us.

Then we can insert our Piece component inside the Square components:

function Square(props) {
    // ...

    return ( 
        <td className={'square ' + (isLight ? 'light' : 'dark')}>
            {props.data.trim() && <Piece data={props.data} /> }
        </td>
    )
}

Note that we don’t want to include a piece in every square, that’s why we are checking that props.data is empty when trimmed for spaces. We do that by taking advantage of javascript lazy evaluation in conditional structure: if the left side of my && (logical AND) evaluates to false, then the right side is not evaluated.

Components are nestable

So far, that could be it for our Checkers board. We could easily add the ‘King’ pieces by creating two more svg images, adding one more condition in our Piece component and modifying the data in our App component…

Did you noticed that the components we would have to modify are at the two extremities of our components stack?

In their best selling book, Refactoring: Improving the Design of Existing Code 5, Martin Fowler and Kent Beck, have labeled this particular anti-pattern shotgun surgery.

Because the related code is spread over the components, if we want to make a change, we will have to search into multiple files for the parts we have to modify. But the code doesn’t have to be written that way.

We could start by moving our Piece component up to the Row:

import Piece from './Piece'

function Row(props) {
    // ...

                return (
                    <Square 
                        key={column + props.number} 
                        row={props.number} 
                        column={column}
                    >
                        {squareData.trim() && <Piece data={squareData} />} 
                    </Square>
                )
    // ...
}

Can you see how the Piece component is nested between the two <Square> tags?

To retrieve it from inside the Square component, we have to access a React components’ special property: children.

function Square(props) {
    // ...

    return ( 
        <td className={'square ' + (isLight ? 'light' : 'dark')}>
            {props.children}
        </td>
    )
}

Now our Square component can display any JSX markup we nest inside it.

But we won’t stop here. In fact we could modify our Row component even further, to not take an array of characters, but directly an array of React components.

function Row(props) {
    return (
        <tr>
            <th>{props.number}</th>
            {props.data.map((Piece, index) => {
                const column = String.fromCharCode(97 + index)

                return (
                    <Square 
                        key={column + props.number} 
                        row={props.number} 
                        column={column}
                    >
                        {Piece && <Piece/>}
                    </Square>
                )
            })}
            <th>{props.number}</th>
        </tr>
    )
}

Here, we don’t import the Piece component from the Piece.js file anymore. Instead we get it by iterating over the data property that is passed to our Row component.

Then, if it is not a falsy value (like null), we compute whatever component it might be. But we cannot pass it a property value from here. Instead, we need to separate our pieces into different components.

import blackMan from '../../assets/black-man.svg'

function BlackMan(props) {
    return (
        <img src={blackMan} alt={'A black man.'} className={'piece'} />
    )
}

import whiteMan from '../../assets/white-man.svg'

function WhiteMan(props) {
    return (
        <img src={whiteMan} alt={'A white man.'} className={'piece'} />
    )
}

And then we change our two-dimensional array from the App component to populate it with function components.

function App() {
  const data = [
    [null, BlackMan, null, BlackMan, null, BlackMan, null, BlackMan],
    [BlackMan, null, BlackMan, null, BlackMan, null, BlackMan, null],
    [null, BlackMan, null, BlackMan, null, BlackMan, null, BlackMan],
    [null, null, null, null, null, null, null, null],
    [null, null, null, null, null, null, null, null],
    [WhiteMan, null, WhiteMan, null, WhiteMan, null, WhiteMan, null],
    [null, WhiteMan, null, WhiteMan, null, WhiteMan, null, WhiteMan],
    [WhiteMan, null, WhiteMan, null, WhiteMan, null, WhiteMan, null]
  ]

  return (
    <table className='no-border'>
      // ...
      <tbody>
        {data.map((rowData, index) => {
          const number = data.length - index
          
          return <Row key={number.toString()} number={number} data={rowData} />
        })}
      </tbody>
      // ...
    </table>
  );
}

Exercise 2: How would you implement the King pieces now?

Source codesolution.

How I learned

I didn’t learn all of this by myself though. I used a shortcut: a React expert’s book.

The book I have used during my very first week of learning React is React Projects6, by Roy Derks. This book has a very practical approach that takes you by the hand through its project based learning. During my 8 first hours with it, I built:

  • A simple movie list.
  • A portfolio that fetches data from the GitHub API.
  • A project management board with a drag’n’drop interface.

Building projects is a great way of learning new technologies and practices. It keeps you motivated by envisioning a goal.

If the board game theme motivates you more, here are some ideas to tweak the above projects:

  • Your favorite board games list
  • Fetching the most popular board games from Board Game Geek’s API7
  • A tournament scheduler where you drag’n’drop the players as matches are played

Enjoy your learning, and nest all those components!


  1. Create React App, accessed December 4, 2020, https://create-reac-app.dev↩︎
  2. NodeJs.org, accessed December 4, 2020, https://nodejs.org↩︎
  3. String.fromCharCode(), JavaScript Reference, Mozilla Developers Network, last modified December 18, 2020, ↩︎
  4. SVG: Scalable Vector Graphics, Mozilla Developers Network, last modified December 30, 2020, https://developer.mozilla.org/en-US/docs/Web/SVG↩︎
  5. Martin Fowler, Kent Beck, Refactoring: Improving the Design of Existing Code, 2nd edition (Boston: Addison-Wesley Professional, 2018).↩︎
  6. Roy Derks. React Projects. Birmingham, UK: Packt Publishing, 2019.↩︎
  7. “BGG XML API2”, Wiki, Board Game Geek,last edited December 28, 2016, https://boardgamegeek.com/wiki/page/BGG_XML_API2↩︎

Leave a Reply

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