When I first learned React, my mind immediately went to the Super Tic Tac Toe game I built three months ago using Vanilla JavaScript. I thought this project was the perfect candidate to refactor into React because of the potential for reusable components and the use of state for the game logic.
Follow along with the code:
Original Project Repo | Deployed Original Project
React Project Repo | Deployed React Project
Game Rules
Components
I began by creating basic React components to match the original HTML, which was a lot of copy and pasted div
elements. It was so quick and easy to dynamically render the nine squares of the exterior grid and nine squares of each interior grid using the .map()
method on an array with nine elements.
const [extValues, setExtValues] = useState([null, null, null, null, null, null, null, null, null]);
return (
<div className="Gameboard">
{
extValues.map((extValue, extIdx) => (
<ExteriorSquare />
))
}
</div>
);
State
I saved these values in state in order to render what is displayed in the square: an 'X', an 'O', or null
to display nothing. Also saved in state is the current player, the exterior grid square the current player can play in, and the game winner. Because these values are saved in state, the components will re-render whenever their value is changed. This replaces the need to manipulate the DOM as I did in the original project.
onClick
In addition to learning React, I also leveled up my JavaScript knowledge since I wrote the original project. In the original, I wrote six (6!!!) separate functions to add or remove click event listeners which provide functionality for the player to place their symbol in the space. In the React version, every interior grid space has the onClick
function, but I used ternary logic to determine if there is no winner yet, the space is currently empty, and the exterior grid space is valid (based on where the previous player played). If this expression evaluates to true, the space is clickable, if not, nothing will happen if the player tries to click on an invalid square.
const handleClick = (index) => {
if (!winner && values[index] === null && currentSquare.includes(extIdx)) {
// functionality to handle click event
}
}
Check for Win
To be honest, I didn't come up with the new code, I turned to Google for help. But googling when you don't know the answer or to find a better, more efficient solution is a valid and necessary skill as a developer. The code in the GIFs above and the code block below are doing the same thing - checking for three matching values to determine if a player has three in a row and won the square. The two GIFs show checking for a win in the interior grid and in the exterior grid. The code below can check both from one function. Yay for efficiency!
const winningPositions = [
[0, 1, 2],
[3, 4, 5],
[6, 7, 8],
[0, 3, 6],
[1, 4, 7],
[2, 5, 8],
[0, 4, 8],
[2, 4, 6]
];
const checkWin = (squares) => {
for (let i = 0; i < winningPositions.length; i++) {
const [a, b, c] = winningPositions[i];
if (squares[a] && squares[a] === squares[b] && squares[a] === squares[c]) {
return squares[a];
}
}
return null;
}
Styling
That's All... For Now!
Thank you for reading about this refactor! If you're just starting to learn development, I would advise you to build projects however you can with your existing knowledge and you can always level up and refactor in the future. Who knows what kind of improvements I'll be able to make in three months!
Top comments (0)