反应记忆游戏:状态更新不同步



我正在尝试创建一个4x4内存游戏。我目前设置的方式是,当点击一个正方形时,该正方形的颜色和索引被发送到";支架";处于状态。

一旦在"0"中有2个对象;保持器";,我检查它们是否匹配颜色,如果匹配,它们保持状态;真";,否则,保持器中的索引被映射到状态并重置为status.clicked=";false";。

在"0"中有2个对象之后;保持器";,我将支架重置为空数组。然而,下一次点击似乎没有在";支架";并且它被完全跳过,这导致所有功能都不对齐。

你知道为什么不起作用吗?非常感谢反馈。

沙箱链接:https://codesandbox.io/s/friendly-bas-v3d80?file=/src/App.js

import React, { useState } from 'react';
import './App.css';
function Box({thing, index, style, updateStatus}){
return(
<button type="button" className="Box" style={style(index)} onClick={() => updateStatus(index)}>
<div>
{thing[index].status}
</div>
</button>
)
}
function App() {
const [status, setStatus] = useState([
{status: null, clicked: false, color: 'red'},
{status: null, clicked: false, color: 'red'},
{status: null, clicked: false, color: 'yellow'},
{status: null, clicked: false, color: 'yellow'},
{status: null, clicked: false, color: 'blue'},
{status: null, clicked: false, color: 'blue'},
{status: null, clicked: false, color: 'orange'},
{status: null, clicked: false, color: 'orange'},
{status: null, clicked: false, color: 'pink'},
{status: null, clicked: false, color: 'pink'},
{status: null, clicked: false, color: 'purple'},
{status: null, clicked: false, color: 'purple'},
{status: null, clicked: false, color: 'white'},
{status: null, clicked: false, color: 'white'},
{status: null, clicked: false, color: 'green'},
{status: null, clicked: false, color: 'green'}
]);
const [holder, setHolder] = useState([
])
function updateStatus(index){
const newStatus = [...status]
newStatus[index].clicked = true
const newHolder = [...holder, {"index": newStatus.indexOf(newStatus[index]), "color": newStatus[index].color}]
setHolder(newHolder)
if (holder.length === 2 && !holderTest(holder)){
const newHolder2 = [...status]
holder.map(holder => {newHolder2[holder.index].clicked = false})
setStatus(newHolder2)
setHolder([])
}
if (holder.length === 2){setHolder([])}
}
console.log(holder.length)
console.log(holder)
console.log(status)
function holderTest(holderToTest){
if (holderToTest[0].color === holderToTest[1].color){return true}
}
function colorChange(index){
return (
{backgroundColor: status[index].clicked ? status[index].color : 'black'})
}
return (
<div>
<div className="row">
<div className="column">
<Box thing={status} index={0} style={colorChange} updateStatus={updateStatus}/>
<Box thing={status} index={1} style={colorChange} updateStatus={updateStatus}/>
<Box thing={status} index={2} style={colorChange} updateStatus={updateStatus}/>
<Box thing={status} index={3} style={colorChange} updateStatus={updateStatus}/>
</div>
<div className="column">
<Box thing={status} index={4} style={colorChange} updateStatus={updateStatus}/>
<Box thing={status} index={5} style={colorChange} updateStatus={updateStatus}/>
<Box thing={status} index={6} style={colorChange} updateStatus={updateStatus}/>
<Box thing={status} index={7} style={colorChange} updateStatus={updateStatus}/>
</div>
<div className="column">
<Box thing={status} index={8} style={colorChange} updateStatus={updateStatus}/>
<Box thing={status} index={9} style={colorChange} updateStatus={updateStatus}/>
<Box thing={status} index={10} style={colorChange} updateStatus={updateStatus}/>
<Box thing={status} index={11} style={colorChange} updateStatus={updateStatus}/>
</div>
<div className="column">
<Box thing={status} index={12} style={colorChange} updateStatus={updateStatus}/>
<Box thing={status} index={13} style={colorChange} updateStatus={updateStatus}/>
<Box thing={status} index={14} style={colorChange} updateStatus={updateStatus}/>
<Box thing={status} index={15} style={colorChange} updateStatus={updateStatus}/>
</div>
</div>
</div>
);
}
export default App;

我认为你最好重新思考一下

https://codesandbox.io/s/red-fire-pg16s

更好的系统是:

  • 要显示数据框
  • 记住当前放置的盒子
  • 记住哪些框已经匹配(我使用Set进行有效的成员身份检查(

然后,当您在React中进行渲染时,您可以使用纯状态驱动的方法。即

  • 此框是否已匹配=>不应该是可点击的,并且应该以真实的颜色显示
  • 此框当前是否已保留=>不应该是可点击的,并且应该以真实的颜色出现。另外,在上面加上一个边界
  • 否则,显示没有颜色的框(在这种情况下为灰色(

此外,现在Box不需要任何数据结构的知识,它总是被明确地告知要做什么。

import React, { useState } from "react";
import "./styles.css";
function Box({ color, index, held, matched, selectHandler }) {
const displayColor = held || matched ? color : "grey";
const onClick = held || matched ? null : () => selectHandler(index);
const style = { backgroundColor: displayColor };
if (held) {
style["border"] = "4px solid black";
}
return (
<button type="button" className="Box" style={style} onClick={onClick}>
<div>&nbsp;&nbsp;</div>
</button>
);
}
const data = [
"red",
"red",
"yellow",
"yellow",
"blue",
"blue",
"orange",
"orange",
"pink",
"pink",
"purple",
"purple",
"white",
"white",
"green",
"green"
];
function App() {
// A record of a box currently being matched
const [heldIndex, setHeldIndex] = useState(null);
// A record of previously matched boxes
const [matched, setMatched] = useState(new Set());
function updateStatus(index) {
// If nothing has been "held" already, "hold" this box
if (heldIndex === null) {
setHeldIndex(index);
// Otherwise, see if it's a match with the held box
} else {
// If it's a match, set both of those as matched
if (data[index] === data[heldIndex]) {
setMatched(new Set([...Array.from(matched), index, heldIndex]));
}
// Match or no match, nothing should be held after an attempted pairing
setHeldIndex(null);
}
}
// I didn't bother laying them out in a grid, I leave that to you
// Previously matched boxes are "matched === true"
// Currently held box is "held === true"
const boxes = data.map((item, index) => {
return (
<Box
key={index}
color={item}
index={index}
held={index === heldIndex}
matched={matched.has(index)}
selectHandler={updateStatus}
/>
);
});
return <div>{boxes}</div>;
}
export default App;

最新更新