From 641efa0664e8dafae38a16151c876ffccd84fa12 Mon Sep 17 00:00:00 2001 From: Kaitlin Bolling Date: Wed, 25 Aug 2021 14:06:05 -0400 Subject: [PATCH 1/4] exercise 04 --- src/__tests__/04.js | 4 ++-- src/exercise/04.js | 40 +++++++++++++--------------------------- 2 files changed, 15 insertions(+), 29 deletions(-) diff --git a/src/__tests__/04.js b/src/__tests__/04.js index c207e0cd2..de36474e2 100644 --- a/src/__tests__/04.js +++ b/src/__tests__/04.js @@ -1,8 +1,8 @@ import * as React from 'react' import {render, screen} from '@testing-library/react' import userEvent from '@testing-library/user-event' -import App from '../final/04' -// import App from '../exercise/04' +// import App from '../final/04' +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..0cc03046f 100644 --- a/src/exercise/04.js +++ b/src/exercise/04.js @@ -4,38 +4,25 @@ import * as React from 'react' 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 + const [squares, setSquares] = React.useState(Array(9).fill(null)); + const nextValue = calculateNextValue(squares); + const winner = calculateWinner(squares); + const status = calculateStatus(winner, squares, nextValue); // 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 + if (winner || squares[square]) { + return; + } + + const squaresCopy = [...squares]; + squaresCopy[square] = nextValue; + setSquares(squaresCopy); } function restart() { - // 🐨 reset the squares - // 💰 `Array(9).fill(null)` will do it! + setSquares(Array(9).fill(null)); } function renderSquare(i) { @@ -48,8 +35,7 @@ function Board() { return (
- {/* 🐨 put the status in the div below */} -
STATUS
+
{status}
{renderSquare(0)} {renderSquare(1)} From 0a46d7a4532c2b98b840434cefd4571183e0ec38 Mon Sep 17 00:00:00 2001 From: Kaitlin Bolling Date: Wed, 25 Aug 2021 16:25:53 -0400 Subject: [PATCH 2/4] exercise 04 extra 1 --- src/__tests__/04.extra-1.js | 4 ++-- src/exercise/04.js | 9 ++++++++- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/src/__tests__/04.extra-1.js b/src/__tests__/04.extra-1.js index f50771832..d62b14abf 100644 --- a/src/__tests__/04.extra-1.js +++ b/src/__tests__/04.extra-1.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-1' -// import App from '../exercise/04' +// import App from '../final/04.extra-1' +import App from '../exercise/04' test('can play a game of tic tac toe', () => { const {container} = render() diff --git a/src/exercise/04.js b/src/exercise/04.js index 0cc03046f..c7c00732a 100644 --- a/src/exercise/04.js +++ b/src/exercise/04.js @@ -4,7 +4,14 @@ import * as React from 'react' function Board() { - const [squares, setSquares] = React.useState(Array(9).fill(null)); + const [squares, setSquares] = React.useState( + () => JSON.parse(window.localStorage.getItem('squares')) || Array(9).fill(null) + ); + + React.useEffect(() => { + window.localStorage.setItem('squares', JSON.stringify(squares)) + }, [squares]); + const nextValue = calculateNextValue(squares); const winner = calculateWinner(squares); const status = calculateStatus(winner, squares, nextValue); From 1dd8ca865f1e55b3bb5114231a91178e893498c3 Mon Sep 17 00:00:00 2001 From: Kaitlin Bolling Date: Wed, 25 Aug 2021 16:29:37 -0400 Subject: [PATCH 3/4] exercise 04 extra 2 --- src/exercise/04.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/exercise/04.js b/src/exercise/04.js index c7c00732a..d0764dc03 100644 --- a/src/exercise/04.js +++ b/src/exercise/04.js @@ -2,10 +2,12 @@ // http://localhost:3000/isolated/exercise/04.js import * as React from 'react' +import { useLocalStorageState } from '../utils'; function Board() { - const [squares, setSquares] = React.useState( - () => JSON.parse(window.localStorage.getItem('squares')) || Array(9).fill(null) + const [squares, setSquares] = useLocalStorageState( + 'squares', + Array(9).fill(null) ); React.useEffect(() => { From f003a5928212198721936957f4ac962e8d763a25 Mon Sep 17 00:00:00 2001 From: Kaitlin Bolling Date: Wed, 25 Aug 2021 17:24:59 -0400 Subject: [PATCH 4/4] exercise 04 extra 3 --- src/__tests__/04.extra-1.js | 4 +- src/__tests__/04.extra-3.js | 4 +- src/__tests__/04.js | 4 +- src/exercise/04.js | 96 +++++++++++++++++++++++-------------- 4 files changed, 67 insertions(+), 41 deletions(-) diff --git a/src/__tests__/04.extra-1.js b/src/__tests__/04.extra-1.js index d62b14abf..f50771832 100644 --- a/src/__tests__/04.extra-1.js +++ b/src/__tests__/04.extra-1.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-1' -import App from '../exercise/04' +import App from '../final/04.extra-1' +// import App from '../exercise/04' test('can play a game of tic tac toe', () => { const {container} = render() 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/__tests__/04.js b/src/__tests__/04.js index de36474e2..c207e0cd2 100644 --- a/src/__tests__/04.js +++ b/src/__tests__/04.js @@ -1,8 +1,8 @@ import * as React from 'react' import {render, screen} from '@testing-library/react' import userEvent from '@testing-library/user-event' -// import App from '../final/04' -import App from '../exercise/04' +import App from '../final/04' +// 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 d0764dc03..a89a5da7a 100644 --- a/src/exercise/04.js +++ b/src/exercise/04.js @@ -4,39 +4,15 @@ import * as React from 'react' import { useLocalStorageState } from '../utils'; -function Board() { - const [squares, setSquares] = useLocalStorageState( - 'squares', - Array(9).fill(null) - ); - - React.useEffect(() => { - window.localStorage.setItem('squares', JSON.stringify(squares)) - }, [squares]); - - const nextValue = calculateNextValue(squares); - const winner = calculateWinner(squares); - const status = calculateStatus(winner, squares, nextValue); - - // 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) { - if (winner || squares[square]) { - return; - } - - const squaresCopy = [...squares]; - squaresCopy[square] = nextValue; - setSquares(squaresCopy); - } - - function restart() { - setSquares(Array(9).fill(null)); - } +function Board(props) { + const { + onClick, + squares, + } = props; function renderSquare(i) { return ( - ) @@ -44,7 +20,6 @@ function Board() { return (
-
{status}
{renderSquare(0)} {renderSquare(1)} @@ -60,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}
    )