You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

React按钮状态控制求助:随机变色、点击交互及通关弹窗实现

Fixing Your React Button State & Random Selection Issue

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?

  1. No State Update Trigger: You're creating new arrays inside changeColorHandle but never assigning them to your state variable (using setBtnArray or similar). React only re-renders when state is updated via its official methods, so your changes aren't showing up at all.
  2. Incorrect map Usage: The map function returns a new array, but your logic returns the full btnArray for 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.
  3. Uncontrolled useEffect Triggers: By including btnArray in 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.
  4. Unfiltered Random Selection: Your rand picks 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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.08 17:52:59