React按钮状态控制求助:随机变色、点击交互及通关弹窗实现
Hey there! Let's work through your problem and get those buttons behaving exactly as you want. First, let's break down what's wrong with your current code, then build a working solution step by step.
What's Wrong With Your Current Code?
- No State Update Trigger: You're creating new arrays inside
changeColorHandlebut never assigning them to your state variable (usingsetBtnArrayor similar). React only re-renders when state is updated via its official methods, so your changes aren't showing up at all. - Incorrect
mapUsage: Themapfunction returns a new array, but your logic returns the fullbtnArrayfor every element except the matching one—this doesn't actually modify the target button correctly. You need to create a copy of the array, update the specific item, then set that as the new state. - Uncontrolled
useEffectTriggers: By includingbtnArrayin the useEffect dependency array, you're triggering a new timeout every time the array changes. This will lead to multiple overlapping timeouts and unexpected button behavior. - Unfiltered Random Selection: Your
randpicks any index 0-3, but you should only select from white (unprocessed) buttons—otherwise you might try to update a button that's already red or blue.
Step-by-Step Solution
Let's build the correct implementation from scratch:
1. Initialize Button State Properly
First, set up your button state with all necessary properties (we'll use isDisabled instead of isDisable for standard React naming):
import { useState, useEffect } from 'react'; function App() { // Initialize 4 buttons: white, disabled by default const [btnArray, setBtnArray] = useState([ { id: 0, color: 'white', isDisabled: true }, { id: 1, color: 'white', isDisabled: true }, { id: 2, color: 'white', isDisabled: true }, { id: 3, color: 'white', isDisabled: true }, ]); const [showWinPopup, setShowWinPopup] = useState(false);
2. Fix Random Button Selection
Create a function that only selects from white buttons and updates state correctly:
const activateRandomWhiteButton = () => { // Filter to get only white (available) buttons const whiteButtons = btnArray.filter(btn => btn.color === 'white'); // If no white buttons left, exit early (we'll handle the win state separately) if (whiteButtons.length === 0) return; // Pick a random index from the eligible white buttons const randomIndex = Math.floor(Math.random() * whiteButtons.length); const targetButtonId = whiteButtons[randomIndex].id; // Update state: create a copy of the array, modify the target button setBtnArray(prevArray => prevArray.map(btn => btn.id === targetButtonId ? { ...btn, color: 'red', isDisabled: false } : btn ) ); };
3. Handle Button Click (Red → Blue)
Add a click handler to turn red buttons blue and disable them:
const handleButtonClick = (buttonId) => { setBtnArray(prevArray => prevArray.map(btn => btn.id === buttonId ? { ...btn, color: 'blue', isDisabled: true } : btn ) ); };
4. Check for Win Condition & Trigger Timers
Use an effect to check if all buttons are blue, and show the popup if so. We'll also handle the timed random button activation here:
useEffect(() => { // Check if every button is blue const allBlue = btnArray.every(btn => btn.color === 'blue'); if (allBlue) { setShowWinPopup(true); } else { // Only trigger the next random button if we haven't won yet const timer = setTimeout(() => { activateRandomWhiteButton(); }, 2000); // Cleanup timer to prevent memory leaks return () => clearTimeout(timer); } }, [btnArray]);
5. Render Buttons & Win Popup
Add the JSX to render everything with basic styling:
return ( <div style={{ padding: '2rem', textAlign: 'center' }}> <div style={{ display: 'flex', gap: '1rem', justifyContent: 'center', marginBottom: '2rem' }}> {btnArray.map(btn => ( <button key={btn.id} style={{ width: '80px', height: '80px', backgroundColor: btn.color, border: '1px solid #000', cursor: btn.isDisabled ? 'not-allowed' : 'pointer', borderRadius: '4px', }} disabled={btn.isDisabled} onClick={() => handleButtonClick(btn.id)} /> ))} </div> {showWinPopup && ( <div style={{ position: 'fixed', top: '50%', left: '50%', transform: 'translate(-50%, -50%)', padding: '2rem', backgroundColor: '#fff', border: '2px solid #000', borderRadius: '8px', fontSize: '1.5rem', boxShadow: '0 0 10px rgba(0,0,0,0.2)', }}> {/* Add your custom icon here */} <p>You won game…!!!</p> </div> )} </div> ); } export default App;
Key Takeaways
- Always use React's state updater functions (like
setBtnArray) to modify state—never mutate the original state directly. - When updating arrays/objects in state, create a copy first (using spread operator or
map) to avoid breaking React's state immutability rules. - Filter your random selection to only target eligible buttons (white ones here) to avoid invalid updates.
- Use useEffect cleanup functions to clear timers and prevent memory leaks.
内容的提问来源于stack exchange,提问作者Rohit

