我遇到了一个我无法弄清楚的问题。我正在构建一个 Wordle 克隆,状态似乎在某些事件上更新,而在其他事件上没有更新,我不太明白为什么。
我有一个 Keyboard
组件,它将 handleKeyClick
作为父组件的 prop,并附加到两个事件处理程序。
父组件
import { Box, Divider, Grid, Typography } from "@mui/material";
import { useState, useEffect, useCallback } from 'react';
import Keyboard from "../Keyboard";
import { v4 as uuid } from 'uuid'
import WordleNotifbar from "../WordleNotifBar";
import Loading from "../Utils/Loading";
import { IGuessState } from "../../types";
interface IGuessGridProps {
addGuess: Function,
guesses: any,
answer: any
}
const GuessGrid = (props: IGuessGridProps) => {
const { addGuess, guesses, answer } = props;
let [notif, setNotif] = useState<boolean>(false);
const [guess, setGuess] = useState<string[]>([]);
const styles = {
input: {
border: ".5px solid white",
height: "50px",
display: "flex",
borderRadius: "5px",
justifyContent: "center",
alignItems: "center",
backgroundColor: "",
color: "white",
},
container: {
minWidth: "300px",
width: "30%",
maxWidth: "450px",
margin: "0 auto",
marginTop: "15px",
},
}
// In the parent component, I have defined the function I'm passing in as a prop as such:
const handleAddCharacter = (char: string) => {
setGuess([...guess, char])
}
// Not fully implemented yet
const handleBackspace = (e: MouseEvent): void => {
e.preventDefault();
setGuess([...guess])
}
const handleSubmit = (): void => {
let word = guess.join('')
if (word.length === answer.length) {
addGuess(word.toLowerCase())
setGuess([]);
}
else {
setNotif(true);
setTimeout(() => {
setNotif(false);
}, 1000)
}
}
if (answer) {
return <>
<Divider />
<Grid container sx={styles.container} >
{answer.split('').map((_: string, index: number) => {
return (<Grid item xs={12 / answer.length} sx={styles.input} key={uuid()}>
<Box>
<Typography>
{guess[index]}
</Typography>
</Box>
</Grid>)
})}
</Grid>
<Keyboard guesses={guesses} answer={answer} handleKeyClick={handleAddCharacter} handleBackspace={handleBackspace} submitFunc={handleSubmit} />
{notif ? <WordleNotifbar message="Not Enough Characters" duration={1000} /> : ""}
</>;
} else {
return <Loading />
}
};
export default GuessGrid;
键盘组件
import { Box, Grid, SxProps, Theme, Typography } from "@mui/material";
import { useCallback, useEffect, useState } from 'react';
import { v4 as uuid } from 'uuid';
import BackspaceIcon from '@mui/icons-material/Backspace';
import React from "react";
interface IKeyboardProps {
guesses: string[],
answer: string,
handleKeyClick: any,
submitFunc: any,
handleBackspace: any
}
const Keyboard = (props: IKeyboardProps) => {
const { guesses, answer, handleKeyClick, submitFunc, handleBackspace } = props
const [guessedLetters, setGuessedLetters] = useState<string[]>();
const topRow = 'qwertyuiop'.toUpperCase().split('');
const middleRow = 'asdfghjkl'.toUpperCase().split('');
const bottomRow = 'zxcvbnm'.toUpperCase().split('');
const allKeys = topRow.concat(middleRow.concat(bottomRow));
// When the component is initialized, I am establishing an event listener in the window for the key press events.
useEffect(() => {
window.addEventListener('keypress', handlePhysicalKeyPress)
}, [])
useEffect(() => {
const allGuessedCharacters = guesses.join('').split('');
const uniqueGuessedCharacters = allGuessedCharacters.filter((val: string, index: number, self) => self.indexOf(val) === index)
setGuessedLetters(uniqueGuessedCharacters);
}, [guesses])
const handleVirtualKeyPress = (e: any) => {
handleKeyClick(e.target.textContent)
}
const handlePhysicalKeyPress = (e: KeyboardEvent) => {
e.preventDefault()
if (allKeys.includes(e.key.toUpperCase())) {
handleKeyClick(e.key.toUpperCase());
}
}
const genKeyStyles = (character: string, _: number): SxProps<Theme> => {
character = character.toLowerCase()
const styles = {
width: character === "bs" || character === "enter" ? "63px" : "33px",
marginX: "1px",
marginY: "1px",
borderRadius: "5px",
height: "50px",
color: "black",
textAlign: "center",
backgroundColor: "#DDD",
display: "flex",
justifyContent: "center",
alignItems: "center",
};
if (guessedLetters) {
if (answer.indexOf(character) >= 0 && guessedLetters.indexOf(character) >= 0) {
styles.backgroundColor = "green"
} else if (answer.indexOf(character) < 0 && guessedLetters.indexOf(character) >= 0) {
styles.backgroundColor = "#777"
}
}
return styles
}
return <Box sx={{ display: "flex", flexDirection: "column", justifyContent: "center", marginTop: "10px", }}>
<Box sx={{ display: "flex", justifyContent: "center" }}>
{topRow.map((letter: string, index: any) => {
return (
<Box sx={genKeyStyles(letter, index)} key={uuid()} onClick={handleVirtualKeyPress}>
<Typography key={uuid()}>{letter}</Typography>
</Box>
)
})}
</Box>
<Box sx={{ display: "flex", justifyContent: "center" }}>
{middleRow.map((letter: string, index: any) => {
return (
<Box sx={genKeyStyles(letter, index)} key={uuid()} onClick={handleVirtualKeyPress}>
<Typography key={uuid()}>{letter}</Typography>
</Box>
)
})}
</Box>
<Box sx={{ display: "flex", justifyContent: "center" }}>
<Box sx={genKeyStyles("enter", 1)} key={uuid()} onClick={submitFunc}>
<Typography key={uuid()}>enter</Typography>
</Box>
{bottomRow.map((letter: string, index: any) => {
return (
<Box sx={genKeyStyles(letter, index)} key={uuid()} onClick={handleVirtualKeyPress}>
<Typography key={uuid()}>{letter}</Typography>
</Box>
)
})}
<Box sx={genKeyStyles("bs", 1)} key={uuid()} onClick={handleBackspace}>
<Typography key={uuid()}><BackspaceIcon /></Typography>
</Box>
</Box>
</Box>
};
export default Keyboard;
发生的情况是,虚拟按键似乎正确更新了状态,但物理按键似乎将状态重置回空数组。我真的无法找出发生这种情况的充分理由。有什么想法吗?我提前感谢您的帮助!
最佳答案
当你这样做时:
useEffect(() => {
window.addEventListener('keypress', handlePhysicalKeyPress)
}, [])
...您正在附加一个特定 handlePhysicalKeyPress
函数作为事件监听器。但是该函数是在每个组件重新渲染时重新创建的,因此您不再引用“当前”函数“版本”(如果您尝试删除它,您将无法删除它,因为它不再是相同的引用) .
因此,实际的监听器是函数的第一个“版本”,它调用 handleKeyClick
属性的第一个“版本”,这是您的函数的第一个“版本” handleAddCharacter
函数,它只知道您的guess
状态的第一个版本...这是一个空数组。
这就是为什么当通过按键执行handlePhysicalKeyPress
时,它会从空数组构建一个新的猜测数组。
虽然您应该避免附加到事件监听器的内容与实际的“渲染时间”函数之间的这种差异,但针对您的具体情况应该有一个非常简单的解决方案:您应该使用 functional form你的状态 setter ,即使它是“第一个版本”,它也应该使用“当前”状态版本:
setGuess((currentGuess) => [...currentGuess, char])
关于javascript - React State 未更新关键事件,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/71010357/