gpt4 book ai didi

reactjs - react useState 不更新

转载 作者:行者123 更新时间:2023-12-05 05:46:22 28 4
gpt4 key购买 nike

我正在学习 React,我知道很多问题都涉及到这个主题,但它们都集中在 useState 的异步特性上。我不确定这是否是这里发生的事情。我也尝试了结合useEffect的版本,结果是一样的。

我有一个组件,我正在听按键 - 用户正在尝试猜测一个词。猜出单词后,word 对象应该被替换为另一个对象,新的拼图开始。

组件使用正确的状态(新单词的字符)呈现,但是当尝试第一次猜测第二个谜题时,word 对象仍然是原始状态对象。

如何正确更新此 word 对象?

CodeSandbox

readme.md 中重现的步骤:

const WordFrame = () => {
const [word, setWord] = useState(() => new Word 'apple');
const [renderedCharacters, setRenderedCharacters] = useState(
word.renderedCharacters
);

const keyDownHandler = (e: KeyboardEvent): void => {
console.log(e.key);
if (letters.includes(e.key)) {
const correctGuess = word.processGuess(e.key);

if (correctGuess) {
setRenderedCharacters([...word.renderedCharacters]);

// moving the following if block into useEffect with dependency on word.isGuessed and renderedCharacters doesn't help
if (word.isGuessed) {
const newWord: Word = new Word('banana');
setWord(newWord);
setRenderedCharacters(newWord.renderedCharacters);
}
}
}
};

useEffect(() => {
document.addEventListener('keydown', keyDownHandler);
return () => document.removeEventListener('keydown', keyDownHandler);
}, []);

// useEffect(() => {
// if (word.isGuessed) {
// const newWord = new Word('banana);
// setWord(newWord);
// setRenderedCharacters(newWord.renderedCharacters);
// }
// }, [word.isGuessed, renderedCharacters]);

return (
<div className='word-frame'>
{renderedCharacters.map((c) => (
<LetterFrame characterValue={c.value} key={c.id} />
))}
</div>
);
};

export default WordFrame;

Word.ts

class Word {
private readonly _unguessedCharacter: string = ' ';
private readonly _characters: string[] = [];
private _guessingIndex = 0;

private _renderedCharacters: Character[] = [];
public get renderedCharacters() {
return this._renderedCharacters;
}

private _isGuessed: boolean = false;
public get isGuessed(): boolean {
return this._isGuessed;
}

constructor(wordString: string) {
console.log(`Word.constructor called with parameter: ${wordString}`);
this._characters = wordString.split('');
this.setRenderedCharacters();
}

public processGuess(letter: string): boolean {
const isSuccessfulGuess = (): boolean =>
this._characters[this._guessingIndex].toLowerCase() ===
letter.toLowerCase();

const successful = isSuccessfulGuess();

if (successful) {
this._guessingIndex++;
this.setRenderedCharacters();
}

if (this._guessingIndex > this._characters.length - 1) {
this._isGuessed = true;
}

return successful;
}

private setRenderedCharacters(): void {
this._renderedCharacters = [];

this._characters.forEach((c, i): void => {
if (i >= this._guessingIndex) {
this._renderedCharacters.push(
new Character(this._unguessedCharacter)
);
} else {
this._renderedCharacters.push(new Character(c));
}
});
}
}

export default Word;

最佳答案

问题是 keyDownHandler 从初始渲染中捕获 word,然后只附加到 keydown 一次。每次 word 更改时,您都需要删除并重新附加 keyDownHandler(请记住,无论何时调用 setWord,React 都会触发重新渲染,其中word 包含新单词值)。

修复方法如下:

const WordFrame = () => {
const [word, setWord] = useState(() => new Word('apple'));

// ...

const keyDownHandler = (e: KeyboardEvent): void => { /* ... */ };

useEffect(() => {
document.addEventListener('keydown', keyDownHandler);
return () => document.removeEventListener('keydown', keyDownHandler);
}, [word]); // <-- add `word` to the dependencies for this effect

// ...
};

现在,每次 WordFrame 重新呈现一个新的 word 时,它都会附加一个捕获最新的 keyDownHandler 闭包的新副本word 的值。

如果您想阅读更多有关此机制的信息,我强烈推荐 Dan Abramov 的(综合)指南 here .您还可以谷歌“stale closure React useEffect”并找到更多好的文章。

关于reactjs - react useState 不更新,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/71199750/

28 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com