Course: Section

Episode: Title

## S03・V18:Time Travel (Part 2 – Showing the Past Moves)

Date Created: July 17th, 2019
Last Updated: 27 days ago

Objectives
1. How the array `map()` method works.
2. Show a list of past moves in the game.
Watch Video
Duration: 6m 47s

### Showing the Past Moves

Since we are recording the tic-tac-toe game’s history, we can now display it to the player as a list of past moves. To render multiple items in React, we can use an array of React elements.

### JavaScript map() Method

We know that JavaScript arrays have a `map()` method. This method performs a specified function on each element of an array, outputting the result to a new array. Let’s show an example of the array `map()` method. Say we have an array of three numbers, which we will print to the console.

``````const numbers = [5, 2, 7];
console.log('numbers:', numbers); // Prints `numbers: [5, 2, 7]```````

We can apply the `map()` method to the `numbers` array. The `map()` method takes a transforming function as its parameter. For example, the transforming function could be a simple function that doubles the input value, and outputs the result.

``````const numbers = [5, 2, 7];
console.log('numbers:', numbers); // Prints `numbers: [5, 2, 7]`
const transformFunction = x => x * 2;numbers.map(transformFunction);``````

With the `map()` method on the `numbers` array, the transforming function is called on each element of the array, and outputs a new array. We will assign the output array to a variable. We will print the `doubled` array to the console.

``````const numbers = [5, 2, 7];
console.log('numbers:', numbers); // Prints `numbers: [5, 2, 7]`
const transformFunction = x => x * 2;
const doubled = numbers.map(transformFunction);console.log('doubled:', doubled); // Prints `doubled: [10, 4, 14]```````

You can see that we have doubled each value of the `numbers` array. Generally, we write the transforming function inline as an anonymous function, rather than referring to a named function. We can now delete our named `transformFunction`.

``````const numbers = [5, 2, 7];
console.log('numbers:', numbers); // Prints `numbers: [5, 2, 7]`
const doubled = numbers.map(x => x * 2);console.log('doubled:', doubled); // Prints `doubled: [10, 4, 14]```````

Now, the transforming function within the `map()` method can actually take a second parameter. The second parameter is the index of the element within the array. Let’s give an example where we output the value of the array index in the transforming function: `numbers.map((x, index) => index)`. We will assign the resulting array to a variable called `indexes`, and we will print it to console.

``````const numbers = [5, 2, 7];
console.log('numbers:', numbers); // Prints `numbers: [5, 2, 7]`
const doubled = numbers.map(x => x * 2);console.log('doubled:', doubled); // Prints `doubled: [10, 4, 14]`
const indexes = numbers.map((x, index) => index);
console.log('indexes:', indexes); // Prints `indexes: [0, 1, 2]```````

You can see that just the index values of the `numbers` array are output.

### Mapping over History

Using the `map` method, we can map our `history` of moves to React elements representing `button`s on the screen.

We will assign the result of mapping over `history` to a variable called `moves`. We will render `moves` under the `status` text.

``````const Game = () => {
const initialHistory = [
{ squares: Array(9).fill(null) },
];
const [history, setHistory] = useState(initialHistory);
const [xIsNext, setXIsNext] = useState(true);

const handleClickEvent = i => {
const currentMove = history[history.length - 1];
const newSquares = [...currentMove.squares];

// Return early if winner declared or square filled
const winnerDeclared = Boolean(calculateWinner(newSquares));
const squareFilled = Boolean(newSquares[i]);
if (winnerDeclared || squareFilled) {
return;
}

newSquares[i] = xIsNext ? 'X' : 'O';
const newMove = { squares: newSquares };
const newHistory = [...history, newMove];
setHistory(newHistory);
setXIsNext(!xIsNext);
};

const moves = history.map(move => {    return (      <button>        Go to move #...      </button>    );  });
const currentMove = history[history.length - 1];
const winner = calculateWinner(currentMove.squares);
const status = winner ?
`Winner: \${winner}` :
`Next player: \${xIsNext ? 'X' : 'O'}`;

return (
<div className="game">
<Board
squares={currentMove.squares}
onClickEvent={i => handleClickEvent(i)}
/>
<div className="status">{status}</div>
{moves}    </div>
);
};``````

The rendered `button` is too small, so let’s change its font size in `index.css`.

src/index.css
``````button {
font-size: 32px;
}``````

Let’s test out the mapping of history moves to buttons in our browser. The buttons are now big enough. However, they are too close to each other. We will render the buttons in an ordered list, wrapped as list items. Then, we can give the list items some padding.

src/index.css
``````li {
}``````

Now, let’s go back to `index.js`.

Wrap the `button` in a list item. Now, wrap the rendered `moves` in an ordered list.

src/index.js
``````const Game = () => {
const initialHistory = [
{ squares: Array(9).fill(null) },
];
const [history, setHistory] = useState(initialHistory);
const [xIsNext, setXIsNext] = useState(true);

const handleClickEvent = i => {
const currentMove = history[history.length - 1];
const newSquares = [...currentMove.squares];

// Return early if winner declared or square filled
const winnerDeclared = Boolean(calculateWinner(newSquares));
const squareFilled = Boolean(newSquares[i]);
if (winnerDeclared || squareFilled) {
return;
}

newSquares[i] = xIsNext ? 'X' : 'O';
const newMove = { squares: newSquares };
const newHistory = [...history, newMove];
setHistory(newHistory);
setXIsNext(!xIsNext);
};

const moves = history.map(move => {
return (
<li>        <button>
{description}
</button>
</li>    );
});

const currentMove = history[history.length - 1];
const winner = calculateWinner(currentMove.squares);
const status = winner ?
`Winner: \${winner}` :
`Next player: \${xIsNext ? 'X' : 'O'}`;

return (
<div className="game">
<Board
squares={currentMove.squares}
onClickEvent={i => handleClickEvent(i)}
/>
<div className="status">{status}</div>
<ol>{moves}</ol>    </div>
);
};``````

Let’s see how it looks in the browser.

### Showing the Move Number

Let’s show the move number in the button. We can make use of the `index` parameter in the `map` method. We will add a `description` string variable, showing the index of the move.

``````  const moves = history.map((move, index) => {
const description = `Go to move #\${index}`;    return (
<li>
<button>
{description}
</button>
</li>
);
});``````

We can see how it looks in the browser. Instead of the first button showing `Go to move #0`, it would be better to display: `Go to game start`. Since the index number `0` becomes `false` when cast to a boolean, we can use a ternary expression to achieve this.

``````  const moves = history.map((move, index) => {
const description = Boolean(index)      ? `Go to move #\${index}`      : `Go to game start`;    return (
<li>
<button>
{description}
</button>
</li>
);
});``````

Let’s see how it looks in the browser.

### Code Snapshot

src/index.js
``````import React, { useState } from 'react';
import ReactDOM from 'react-dom';

import './index.css';

const Square = props => {
return (
<button
className="square"
onClick={props.onClickEvent}
>
{props.value}
</button>
);
};

const Board = props => {
const renderSquare = i => {
return (
<Square
value={squares[i]}
onClickEvent={() => handleClickEvent(i)}
/>
);
};

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

const Game = () => {
const initialHistory = [
{ squares: Array(9).fill(null) },
];
const [history, setHistory] = useState(initialHistory);
const [xIsNext, setXIsNext] = useState(true);

const handleClickEvent = i => {
const currentMove = history[history.length - 1];
const newSquares = [...currentMove.squares];

// Return early if winner declared or square filled
const winnerDeclared = Boolean(calculateWinner(newSquares));
const squareFilled = Boolean(newSquares[i]);
if (winnerDeclared || squareFilled) {
return;
}

newSquares[i] = xIsNext ? 'X' : 'O';
const newMove = { squares: newSquares };
const newHistory = [...history, newMove];
setHistory(newHistory);
setXIsNext(!xIsNext);
};

const moves = history.map((move, index) => {
const description = Boolean(index)
? `Go to move #\${index}`
: `Go to game start`;
return (
<li>
<button>
{description}
</button>
</li>
);
});

const currentMove = history[history.length - 1];
const winner = calculateWinner(currentMove.squares);
const status = winner ?
`Winner: \${winner}` :
`Next player: \${xIsNext ? 'X' : 'O'}`;

return (
<div className="game">
<Board
squares={currentMove.squares}
onClickEvent={i => handleClickEvent(i)}
/>
<div className="status">{status}</div>
<ol>{moves}</ol>
</div>
);
};

ReactDOM.render(
<Game />,
document.getElementById('root')
);

function calculateWinner(squares) {
/*
0 1 2
3 4 5
6 7 8
*/
const lines = [
[0, 1, 2],
[3, 4, 5],
[6, 7, 8],
[0, 3, 6],
[1, 4, 7],
[2, 5, 8],
[0, 4, 8],
[2, 4, 6],
];

for (let line of lines) {
const [a, b, c] = line;
if (
squares[a] &&
squares[a] === squares[b] &&
squares[a] === squares[c]
) {
return squares[a];
}
}

return null;
}``````
src/index.css
``````body {
background-color: #444;
color: white;
margin: 20px;
font: 32px "Century Gothic", Futura, sans-serif;
}

button {
font-size: 32px;
}

li {
}

.game {
display: flex;
flex-direction: column;
align-items: center;
}

.board-row {
display: flex;
flex-flow: row nowrap;
}

.square {
background-color: #444;
color: white;
border: 1px solid #999;
font-size: 84px;
text-align: center;
width: 100px;
height: 100px;
}

.square:focus {
outline: none;
background-color: #222;
}

.status {
margin: 20px;
}``````

### Summary

We explained how the array `map()` method works. This enabled us to show past moves in the game as buttons labeled with their index number.

### Next Up…

In the next video, we will explain why it is important to add a unique `key` to list elements in React.

Previous Video
Next Video