React猜词游戏点击状态管理异常:字母点击后无法禁用
Let's break down the root causes of your problems and walk through concrete fixes:
Problem Breakdown & Solutions
1. The else if(clicked) branch never executes
This happens because your useEffect immediately resets the clicked state: when you click a letter, disableLetter sets clicked=true, but the useEffect (which depends on clicked) runs right after and calls setClicked(false). This means clicked flips back to false instantly, so the else if(clicked) branch never gets a chance to render.
Additionally, running props.correct()/props.incorrect() directly in the component's render phase is a mistake—this will trigger those callbacks every time the component re-renders, leading to unnecessary state updates and potential bugs.
2. disable={clicked} doesn't work on button elements
First, React uses the camelCase property disabled (note the trailing "d") for disabling buttons, not disable. Second, even if you used the correct property name, the quick reset of clicked to false means the condition never stays true long enough to disable the button.
3. Removing setClicked(false) causes infinite loops
Without resetting clicked, the state stays true, and your render-phase if(clicked) check keeps calling props.correct()/props.incorrect(). These callbacks trigger state updates in the parent component, which re-renders the AvailableLetter child. The child then re-runs the if(clicked) check, triggering another parent update—creating an infinite loop.
Full Fixed Code
Updated AvailableLetter.js
We'll move the guess logic to the click handler, remove the broken useEffect, and keep the clicked state persistent after a click:
import React, { useState } from 'react'; import classes from './AvailableLetter.module.css'; import Ax from '../../hoc/Ax'; const AvailableLetter = (props) => { const [clicked, setClicked] = useState(false); // Handle click and guess logic in one place const handleLetterClick = () => { if (clicked) return; // Prevent duplicate clicks setClicked(true); // Mark letter as clicked const isCorrect = props.play(); // Trigger parent callbacks only once on click if (isCorrect) { props.correct(props.alphabet); } else { props.incorrect(); } props.setSolved(); }; // Build class list dynamically const attachedClasses = [classes.AvailableLetter]; if (clicked) { attachedClasses.push(classes.Disabled); } // Single element render (no need for if/else branches) const letter = ( <span onClick={handleLetterClick} className={attachedClasses.join(' ')} // If using a button instead, use this code: // <button // onClick={handleLetterClick} // className={attachedClasses.join(' ')} // disabled={clicked} // > > {props.alphabet} </span> ); return ( <Ax> {letter} </Ax> ); }; export default AvailableLetter;
Quick Note for Letters.js
Make sure your parent component's correct handler accepts a letter parameter (since we're passing props.alphabet now):
// Example parent component correct handler const correctHandler = (guessedLetter) => { // Logic to update solved letters, e.g., add guessedLetter to a solved set };
This fixes all three issues:
- Clicked letters stay disabled (the
Disabledclass is applied and stays applied) - Buttons will properly disable when using
disabled={clicked} - No more infinite loops, since we only trigger parent callbacks once per click (not on every render)
内容的提问来源于stack exchange,提问作者Logan Lee

