Есть две небольшие проблемы, которые необходимо исправить.Вы правильно отреагировали.Проблемы заключаются в следующем:
- При щелчке по дескриптору вам нужно создать новый массив, а не использовать старый.
- Параметры среза должны отличаться в истории дескриптора, чтобы получитьжелаемый вывод.
Добавили комментарии в ваш код для понимания.Надеюсь, это будет полезно для вас.
const { useState, useEffect } = React
const App = props => {
const [turn, setTurn] = useState("X");
const [history, setHistory] = useState([Array(9).fill(null)]);
const [winner, setWinner] = useState(null);
const latestBoard = history[history.length - 1];
const handleClick = i => {
if (latestBoard[i] !== null) {
return;
}
//latestBoard.splice(i, 1, turn);
//setHistory([...history, [...latestBoard]]);
// latestBoard and history are same
// reference hence you are spreading the same array
// you need to do something like this below
const copylatestBoard = [...latestBoard]; // Because array are passed by
// reference 1st change
copylatestBoard.splice(i, 1, turn); // 2nd change
setHistory([...history, [...copylatestBoard]]); //3rd change
setTurn(turn === "O" ? "X" : "O");
};
const handleHistory = index => {
// if (index === 1) {
// setHistory([Array(9).fill(null)]);
// return;
// }
let newHistory = history.slice(0, index + 1); // need to be index + 1 to slice desired
// as history at 0 is blank board. I am sure it will be obvious now
setHistory(newHistory);
};
const calcWinner = squares => {
/*...*/
};
return (
<div className="container">
<GameBoard onClick={idx => handleClick(idx)} squares={latestBoard} />
<Sidebar
turn={turn}
history={history}
goBack={idx => handleHistory(idx)}
/>
</div>
);
};
const GameBoard = ({ squares, onClick }) => {
const grid = squares.map((item, idx) => {
return (
<div key={idx} onClick={() => onClick(idx)}>
{item}
</div>
);
});
return <div className="grid-container">{grid}</div>;
};
const Sidebar = ({ winner, turn, history, goBack }) => {
let historyList = history.map((pos, idx) => {
return idx >= 1 ? ( // to start after the first click
<li key={idx} onClick={() => goBack(idx)}>
{idx}
</li>
) : null;
});
let info;
if (winner) {
info = `The winner is ${winner}`;
} else {
info = `${turn} it's your turn`;
}
return (
<div className="sidebar">
<div className="gameInfo">{info}</div>
<ul className="history-dropdown"> Go to move {historyList}</ul>
</div>
);
};
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
body {
font-family: sans-serif;
text-align: center;
display: flex;
justify-content: center;
}
.container {
border: 1px solid black;
width: 25em;
height: 90vh;
max-height: 18em;
display: flex;
justify-content: center;
}
.container > * {
border: 1px solid black;
margin: 1em;
}
.grid-container {
flex: 5;
display: grid;
grid-template-columns: 1fr 1fr 1fr;
grid-template-rows: 1fr 1fr 1fr;
grid-template-areas: ". . ." ". . ." ". . .";
position: relative;
height: 80%;
}
.grid-container > * {
border: 1px solid black;
}
.grid-container > *:hover {
cursor: pointer;
}
.sidebar {
padding: 1em;
}
.sidebar .history-dropdown {
transition: all 600ms ease-in-out;
}
.sidebar .history-dropdown > *:hover {
cursor: pointer;
border: 1px solid black;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.6/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.6/umd/react-dom.production.min.js"></script>
<div id="root"></div>