КрестикиНолики. При быстрых ходах, клетки перестают работать
Вроде обычная задача, но почему то застрял. И да, иногда нужно постараться чтобы игра сломалась, игр 4-5 пощелкайте пожалуйста, и по истории можно побегать или рестарты пощелкать чтобы добиться нерабочий случай. Ссылка на игру https://react-hooks.netlify.app/isolated/final/04.extra-3.js
Код:
function useLocalStorageState(
key,
defaultValue = '',
{serialize = JSON.stringify, deserialize = JSON.parse} = {},
) {
const [state, setState] = React.useState(() => {
const valueInLocalStorage = window.localStorage.getItem(key)
if (valueInLocalStorage) {
console.log('deserialize', valueInLocalStorage, key)
return deserialize(valueInLocalStorage)
} else {
console.log('defaultValue')
return typeof defaultValue === 'function' ? defaultValue() : defaultValue
}
})
const prevKeyRef = React.useRef(key)
React.useEffect(() => {
const prevKey = prevKeyRef.current
if (prevKey !== key) {
window.localStorage.removeItem(prevKey)
}
prevKeyRef.current = key
window.localStorage.setItem(key, serialize(state))
}, [key, state, serialize])
return [state, setState]
}
import * as React from 'react'
import {useLocalStorageState} from '../utils'
function Board({squares, onClick}) {
const renderSquare=async(i)=> {
return await (
<button className="square" onClick={() => onClick(i)}>
{squares[i]}
</button>
)
}
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>
)
}
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]
console.log('currentSquares has rendered', currentSquares)
const winner = calculateWinner(currentSquares)
const nextValue = calculateNextValue(currentSquares)
const status = calculateStatus(winner, currentSquares, nextValue)
const selectSquare = async square => {
if (winner || currentSquares[square]) {
return
}
const newHistory = await history.slice(0, currentStep + 1)
const squares = await [...currentSquares]
squares[square] = await nextValue
await setHistory([...newHistory, squares])
await setCurrentStep(newHistory.length)
console.log('click')
}
function restart() {
setHistory([Array(9).fill(null)])
setCurrentStep(0)
console.log(currentSquares, 'squares after restart')
}
const moves = history.map((stepSquares, step) => {
const desc = step ? `Go to move #${step}` : 'Go to game start'
const isCurrentStep = step === currentStep
return (
<li key={step}>
<button disabled={isCurrentStep} onClick={() => setCurrentStep(step)}>
{desc} {isCurrentStep ? '(current)' : null}
</button>
</li>
)
})
return (
<div className="game">
<div className="game-board">
<Board onClick={selectSquare} squares={currentSquares} />
<button className="restart" onClick={restart}>
restart
</button>
</div>
<div className="game-info">
<div>{status}</div>
<ol>{moves}</ol>
</div>
</div>
)
}
function calculateStatus(winner, squares, nextValue) {
return winner
? `Winner: ${winner}`
: squares.every(Boolean)
? `Scratch: Cat's game`
: `Next player: ${nextValue}`
}
function calculateNextValue(squares) {
const xSquaresCount = squares.filter(r => r === 'X').length
const oSquaresCount = squares.filter(r => r === 'O').length
return oSquaresCount === xSquaresCount ? 'X' : 'O'
}
function calculateWinner(squares) {
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 i = 0; i < lines.length; i++) {
const [a, b, c] = lines[i]
if (squares[a] && squares[a] === squares[b] && squares[a] === squares[c]) {
return squares[a]
}
}
return null
}
function App() {
return <Game />
}
export default App