reactjs - 意外的 useEffect 行为
问题描述
我正在制作刽子手游戏。当字母被点击时,它不应该再被点击了。
这是关于代码沙盒的简化版本: https ://codesandbox.io/s/intelligent-cori-6b80q
在沙箱中,当单击字母时,它会变为“CLICKED!”。这里的一切似乎都很好。
但在我的项目中,我得到了奇怪的行为。
可用Letter.js
const AvailableLetter = (props) => {
const [clicked,setClicked]=useState(false);
const setStuff = () => {
setClicked(false); // If I delete this I get Error: Maximum update depth exceeded.
props.setSolved();
};
useEffect( setStuff,[clicked] );
if (clicked) // IF CLICKED REMAINS TRUE, EVERY LETTER GETS PLAYED.
{
if (props.play())
{
props.correct();
}
else
{
props.incorrect();
}
}
const disableLetter = () => {
setClicked(true);
};
let letter=span onClick={disableLetter}>{props.alphabet}</span>;
if(clicked) // CODE NEVER GETS HERE!!!!!!!!!!!!!!
{
letter = <span>{props.alphabet}</span>;
}
return (
<Ax> // Ax is a high order class, just a wrapper
{letter}
</Ax>
);
}
即使单击后,字母仍可单击。这不是我想要的。
每个字母都由 Letters.js 呈现,它在 az 中提供并生成自定义的 AvailableLetter。
const availableLetters = [ ...allLetters ].map(
(alphabet,i) => {
return (
<AvailableLetter setSolved={props.setSolved} play={()=>playHandler(alphabet)} correct={()=>props.correct(alphabet)} incorrect={()=>props.incorrect(alphabet)} solution={props.solution} key={i} alphabet={alphabet} />
);
}
);
所以这里要解决的问题是:
- Letters remain clickable even after a click
- If setClicked(false) is removed it causes an infinite loop
const setStuff = () => {
setClicked(false); // if removed causes infinite loop
props.setSolved();
};
所有这一切都很奇怪,因为在代码和框中我不需要在 setEffect() 中将 clicked 设置为 false。
你可以在这里看到所有的代码:https ://github.com/guntinug/hangmanerrors/tree/master/src 请看一下项目代码,它不长。
解决方案
这是一个结构性问题,我不得不将处理函数和状态委托给父组件 Letters.js
我必须让它如丝般顺滑:p
import React, {useState} from 'react';
import AvailableLetter from './AvailableLetter/AvailableLetter';
import DisabledLetter from './DisabledLetter/DisabledLetter';
import classes from './Letters.module.css';
const Letters = (props) => {
const [lettersMap, setLettersMap]=useState(
{
"a":false,"b":false,"c":false,"d":false,"e":false,"f":false,"g":false,"h":false,"i":false,"j":false,"k":false,"l":false,"m":false,"n":false,"o":false,"p":false,"q":false,"r":false,"s":false,"t":false,"u":false,"v":false,"w":false,"x":false,"y":false,"z":false
}
);
const updateClickedHandler = (letter) => {
setLettersMap(
{
...lettersMap,[letter]:true
}
);
};
const playHandler = (alphabet) => {
const solution = props.solution.split('');
console.log(solution);
if (solution.indexOf(alphabet)<0)
{
console.log('incorrect');
return false;
}
else
{
console.log('correct');
return true;
}
}
const renderedLetters = Object.keys(lettersMap).map(
(letter,i)=>{
if (!lettersMap[letter]) //letter is not yet clicked
{
return (
<AvailableLetter updateClicked={updateClickedHandler} setSolved={props.setSolved} play={()=>playHandler(letter)} correct={()=>props.correct(letter)} incorrect={()=>props.incorrect(letter)} solution={props.solution} key={i} alphabet={letter} />
)
}
else //letter is clicked
{
return (
<DisabledLetter alphabet={letter} />
)
}
}
);
return (
<div className={classes.Letters}>
<p>Solution: {props.solution}</p>
<div className={classes.AvailableLetters}>
{renderedLetters}
</div>
</div>
);
}
export default Letters;
和
import React from 'react';
import classes from './AvailableLetter.module.css';
import Ax from '../../hoc/Ax';
const AvailableLetter = (props) => {
const setStuff = () => {
if (props.play()) {
props.correct();
}
else {
props.incorrect();
}
props.updateClicked(props.alphabet);
props.setSolved();
};
return (
<Ax>
<span className={classes.AvailableLetter} onClick={setStuff}>{props.alphabet}</span>
</Ax>
);
}
export default AvailableLetter;
最后,我添加了另一个名为 DisabledLetter.js 的组件
import React from 'react';
import classes from './DisabledLetter.module.css';
const DisabledLetter = (props) => {
return (
<span className={classes.DisabledLetter} >{props.alphabet}</span>
);
};
export default DisabledLetter;
您可以在此处查看整个源代码 https://github.com/guntinug/hangmanclicked/tree/master/src ;)
但是发现了一个问题,当游戏解决后,我需要再单击一个字母才能显示“你赢了”消息。
当我死时会立即显示“游戏结束”。
推荐阅读
- javascript - 使用 JavaScript 检测浏览器 ClickOnce 支持
- python - Python数据框根据其他id列创建索引列
- c# - 选定的单选按钮在 iText.Html2pdf 中不起作用
- c# - 将八位字节流base64字符串转换为图像
- html - Ill_Keeping SVG 中文本元素的结构
- ssh - 鳄梨酱 SFTP 不适用于较大的文件
- spring-security - 在集群环境中管理 Json Web Key Set
- html - 角度 - 仅在页面刷新时触发的路由动画
- wordpress - Wordpress - 具有多个键的自定义字段元查询
- azure - 只能在草稿应用上创建状态为草稿的版本。如何告诉 Azure DevOps 将发布标记为“草稿”?