diff --git a/src/__tests__/04.extra-3.js b/src/__tests__/04.extra-3.js index cf92205e6..e49212d11 100644 --- a/src/__tests__/04.extra-3.js +++ b/src/__tests__/04.extra-3.js @@ -2,8 +2,8 @@ import * as React from 'react' import {alfredTip} from '@kentcdodds/react-workshop-app/test-utils' import {render, screen} from '@testing-library/react' import userEvent from '@testing-library/user-event' -import App from '../final/04.extra-3' -// import App from '../exercise/04' +// import App from '../final/04.extra-3' +import App from '../exercise/04' test('can play a game of tic tac toe', () => { render() diff --git a/src/exercise/04.js b/src/exercise/04.js index a0b7efe50..a89a5da7a 100644 --- a/src/exercise/04.js +++ b/src/exercise/04.js @@ -2,45 +2,17 @@ // http://localhost:3000/isolated/exercise/04.js import * as React from 'react' +import { useLocalStorageState } from '../utils'; -function Board() { - // 🐨 squares is the state for this component. Add useState for squares - const squares = Array(9).fill(null) - - // 🐨 We'll need the following bits of derived state: - // - nextValue ('X' or 'O') - // - winner ('X', 'O', or null) - // - status (`Winner: ${winner}`, `Scratch: Cat's game`, or `Next player: ${nextValue}`) - // 💰 I've written the calculations for you! So you can use my utilities - // below to create these variables - - // This is the function your square click handler will call. `square` should - // be an index. So if they click the center square, this will be `4`. - function selectSquare(square) { - // 🐨 first, if there's already winner or there's already a value at the - // given square index (like someone clicked a square that's already been - // clicked), then return early so we don't make any state changes - // - // 🦉 It's typically a bad idea to mutate or directly change state in React. - // Doing so can lead to subtle bugs that can easily slip into production. - // - // 🐨 make a copy of the squares array - // 💰 `[...squares]` will do it!) - // - // 🐨 set the value of the square that was selected - // 💰 `squaresCopy[square] = nextValue` - // - // 🐨 set the squares to your copy - } - - function restart() { - // 🐨 reset the squares - // 💰 `Array(9).fill(null)` will do it! - } +function Board(props) { + const { + onClick, + squares, + } = props; function renderSquare(i) { return ( - ) @@ -48,8 +20,6 @@ function Board() { return (
- {/* 🐨 put the status in the div below */} -
STATUS
{renderSquare(0)} {renderSquare(1)} @@ -65,18 +35,69 @@ function Board() { {renderSquare(7)} {renderSquare(8)}
-
) } function Game() { + const [history, setHistory] = useLocalStorageState( + 'tic-tac-toe:history', + [Array(9).fill(null)] + ); + + const [currentStep, setCurrentStep] = useLocalStorageState( + 'tic-tac-toe:step', + 0, + ); + + const currentSquares = history[currentStep]; + const nextValue = calculateNextValue(currentSquares); + const winner = calculateWinner(currentSquares); + const status = calculateStatus(winner, currentSquares, nextValue); + + function selectSquare(square) { + if (winner || currentSquares[square]) { + return; + } + + const newHistory = history.slice(0, currentStep + 1); + const squares = [...currentSquares]; + squares[square] = nextValue; + setHistory([...newHistory, squares]); + setCurrentStep(currentStep + 1); + } + + const moves = history.map((stepSquares, i) => { + const buttonText = (i === 0) ? 'Go to game start' : `Go to move #${i}`; + const isCurrentStep = i === currentStep; + return ( +
  • + +
  • + ); + }); + + function restart() { + setHistory([Array(9).fill(null)]); + setCurrentStep(0); + } + return (
    - + + +
    +
    +
    {status}
    +
      {moves}
    )