Я сделал игру в крестики-нолики (собственная документация React), теперь я пытаюсь изменить некоторые функции, в этом случае первое, что я изменил, - это то, что пользователь может наблюдать за предыдущими ходами после завершения игры. Я получил результат, который искал, но мне нужно щелкнуть еще раз, чтобы изменить значение победителя и, следовательно, чтобы появились нижние кнопки (те, которые позволяют пролистывать историю игры, перемещаются ходом). Вы знаете, что вызывает это? Как я могу это исправить?
Вот мой код: (Я пробовал несколько решений, но ни одно из них не сработало, я понятия не имею, что вызывает эту проблему, и поэтому я помещаю весь код)
function Square(props) {
return (
<button className = "square" onClick = {props.onClick}>
{props.value}
</button>
);
}
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;
class Board extends React.Component {
renderSquare(i) {
return <Square value = {this.props.squares[i]}
onClick = {()=>{this.props.onClick(i)}}/>;
}
render() {
return (
<div>
<div className = "board-row">
{this.renderSquare(0)}
{this.renderSquare(1)}
{this.renderSquare(2)}
</div>
<div className = "board-row">
{this.renderSquare(3)}
{this.renderSquare(4)}
{this.renderSquare(5)}
</div>
<div className = "board-row">
{this.renderSquare(6)}
{this.renderSquare(7)}
{this.renderSquare(8)}
</div>
</div>
);
}
}
class Game extends React.Component {
constructor(props) {
super(props)
this.state = {
history: [{
squares: Array(9).fill(null)
}],
stepNumber: 0,
xIsNext: true,
winner: null
}
};
handleClick(i) {
const history = this.state.history.slice(0, this.state.stepNumber + 1);
const current = history[history.length - 1];
const squares = current.squares.slice();
if (calculateWinner(squares) || squares[i]) {
return;
}
squares[i] = this.state.xIsNext ? 'X' : 'O';
this.setState({
history: history.concat([{
squares: squares,
}]),
stepNumber: history.length,
xIsNext: !this.state.xIsNext,
});
}
jumpTo(step) {
this.setState({
stepNumber: step,
xIsNext: (step % 2) === 0,
});
}
checkForWinner(squares,winr) {
if (winr === null)
this.setState({
winner: calculateWinner(squares)
})
}
render() {
const history = this.state.history;
const current = history[this.state.stepNumber];
const winner = this.state.winner
const moves = history.map((_, move) => {
const desc = move ?
'Go to move #' + move :
'Go to game start';
return (
<li key = {move}>
<button onClick = {() => this.jumpTo(move)}>{desc}</button>
</li>
);
});
return (
<div className = "game">
<div className = "game-board">
<Board
squares = {current.squares}
onClick = {i => {
if (!winner) {
this.handleClick(i)
}
this.checkForWinner(current.squares, winner)
}}
/>
</div>
<div className = "game-info">
<div>{(winner) ? 'Winner: ' + winner : 'Next player: ' + (this.state.xIsNext ? 'X' : 'O')}</div>
<ol>{(winner) ? moves : null}</ol>
</div>
</div>
);
}
}
ReactDOM.render(
<Game />,
document.getElementById('root')
);



![Безумие обратных вызовов в javascript [JS]](https://i.imgur.com/WsjO6zJb.png)


Когда вы проверяете победителя, хотя он должен быть победителем, квадраты еще не обновляются (this.handleClick действительно меняет состояние, но само изменение является асинхронным).
Я бы рекомендовал переместить проверку победителя в обработчик кликов
handleClick(i) {
const history = this.state.history.slice(0, this.state.stepNumber + 1);
const current = history[history.length - 1];
const squares = current.squares.slice();
if (squares[i]) {
return;
}
squares[i] = this.state.xIsNext ? "X" : "O";
this.setState({
history: history.concat([
{
squares: squares
}
]),
stepNumber: history.length,
xIsNext: !this.state.xIsNext,
winner: calculateWinner(squares) // <-- this
});
}
https://codesandbox.io/s/epic-sutherland-gj6d5?file=/src/App.js:1996-2028
Рад слышать. Не за что :)