class App extends React.Component {
constructor() {
super();
this.state = {
currentPlayer: false, //false is X, true is O
message: null,
gameOver: false,
grids : {
'0': null,
'1': null,
'2': null,
'3': null,
'4': null,
'5': null,
'6': null,
'7': null,
'8': null
}
}
this.baseState = this.state
}
restartGame = () => {
this.setState(this.baseState);
}
updateGridHandler = (index) => {
let updatedGrids = {...this.state.grids};
updatedGrids[index] = this.state.currentPlayer;
this.setState(prevState => ({currentPlayer: !prevState.currentPlayer,
grids: updatedGrids}),
() => {
this.checkDraw();
this.checkWin();
});
}
checkWin = () => {
const winningCombination = [
[0, 1, 2],
[3, 4, 5],
[6, 7, 8],
[0, 3, 6],
[1, 4, 7],
[2, 5, 8],
[2, 4, 6],
[0, 4, 8],
];
for (let i = 0; i < winningCombination.length; i++) {
const [a, b, c] = winningCombination[i];
if (this.state.grids[a] && this.state.grids[b] &&
this.state.grids[c]) {
console.log('O won');
}
else if (this.state.grids[a] === false &&
this.state.grids[b] === false &&
this.state.grids[c] === false) {
console.log('X won');
}
}
}
checkDraw = () => {
for(let grid in this.state.grids)
if(this.state.grids[grid] === null) return false;
}
render() {
return (
<div className="App">
<header className="Header">
<h1>Tic Tac Toe</h1>
<Board grids={this.state.grids}
player={this.state.currentPlayer}
updateGrid={(index) => this.updateGridHandler(index)}
/>
<Button reset={this.restartGame}/>
</header>
</div>
);
}
}
class Board extends React.Component {
clickHandler = (index, event) => {
if (this.props.grids[index] === null) {
//update function callbacks
this.props.updateGrid(index);
}
}
hoverNextPlayer = (index, event) => {
let classesForCircle = ["Grid", "Circle"];
let classesForX = ["Grid", "X"];
if (this.props.grids[index] === null) {
if (this.props.player) {
event.target.className = classesForCircle.join(' ')
} else {
event.target.className = classesForX.join(' ')
}
}
}
stopHoverNextPlayer = (index, event) => {
if (this.props.grids[index] === null) {
event.target.className = "Grid";
}
}
render() {
let grids = Object.values(this.props.grids)
.map((value, index) => <Grid
clicked={(event) => this.clickHandler(index, event)}
hovered={(event) => this.hoverNextPlayer(index, event)}
stopHovered={(event) => this.stopHoverNextPlayer(index, event)}
key={index} value={value}
currentPlayer={this.props.player}/>
);
return (
<div className="Board" >
{grids}
</div>
);
}
}
const Grid = (props) => {
return (
<div
onClick={props.clicked}
className="Grid"
onMouseOver={props.hovered}
onMouseLeave={props.stopHovered}>
{props.value}
</div>
);
}
const Button = (props) => {
return (
<div>
<button onClick={props.reset}>Restart
</button>
</div>
);
}
ReactDOM.render(<App />, document.getElementById('main'));
.App {
text-align: center;
}
.Header {
background-color: #282c34;
min-height: 100vh;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
font-size: calc(10px + 2vmin);
color: white
}
.App-link {
color: #61dafb;
}
.Board {
padding: 5px;
margin: auto;
width: 270px;
height: 270px;
display: grid;
justify-content: center;
grid-template-columns: repeat(3, auto);
}
:root {
--grid-size: 90px;
--grid-content: calc(var(--grid-size) * 0.9);
}
.Grid {
box-sizing: border-box;
width: var(--grid-size);
height: var(--grid-size);
border: 1px solid #000;
cursor: pointer;
display: flex;
justify-content: center;
align-items: center;
}
.Grid:hover {
border: 3px solid black;
}
.Grid.X::before,
.Grid.X::after
{
position: absolute;
content: '';
background-color: lightgrey;
width: calc(var(--grid-content)*0.1);
height: var(--grid-content);
}
.Grid.X::before {
transform: rotate(45deg);
}
.Grid.X::after {
transform: rotate(-45deg);
}
.Grid.Circle::before,
.Grid.Circle::after {
position: absolute;
content: '';
border-radius: 50%;
}
.Grid.Circle::before {
width: calc(var(--grid-content)*0.9);
height: calc(var(--grid-content)*0.9);
background-color: lightgrey;
}
.Grid.Circle::after {
width: calc(var(--grid-content)*0.7);
height: calc(var(--grid-content)*0.7);
background-color: #282c34;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
<div id="main"></div>
我正在制作井字游戏。除重新启动按钮外,其他所有功能均正常运行。 Onclick我将状态重置为初始状态,但是我的网格组件在props更改时没有更新。 它将悬停效果带到我的网格上,以显示下一个玩家在悬停上签名...
当玩家获胜或平局时,将渲染Button组件。 Onclick会将状态设置为初始状态。因此,所有网格都声明:null,但不会更新UI(不会删除类名X或圆形)。在网格组件中,{props.value}也会更新为null。所有网格显示最后一个游戏的最后位置,直到我将鼠标悬停在它们上方或悬停它们为止。这就是我可以将UI清除回空网格的方式。如何获取Grid组件以根据道具更改进行更新?
谢谢!
答案 0 :(得分:0)
这里出现的问题可能是因为引用复制是在this.baseState = this.state
中进行的,而不是按值复制。
因此,每当您尝试使用baseState
重置状态时,都不会发生任何变化,因为baseState
的最新值为state
。
尝试一下,按值复制:
this.baseState = {...state}
P.S这仅适用于浅表副本。如果您打算进行深拷贝,请尝试以下What is the most efficient way to deep clone an object in JavaScript?
中的一个选项答案 1 :(得分:0)
我花了很多时间来弄清楚我们的意思,但这实际上是错误的:
Object.values(this.props.grids)
.map((value, index) => <Grid value={value}/>
);
因为我无法映射这样的对象。这是正确的:
Object.keys(this.props.grids)
.map((gridKey, index) => <Grid value={this.props.grids[gridKey] />
在此位置并在我的Grid组件中添加条件语句
<div className={(props.value === null) ?
blank : (props.value === true) ?
classesForCircle.join(' ') : classesForX.join(' ')
}>...</div>
使用重新启动按钮可以正常工作。