React. Почему по истечению таймера кнопки не переключаются обратно?

Когда я нажимаю на кнопку - она становиться disabled. По истечению таймера, кнопка должна быть снова активна, но этого не происходит. Почему кнопки не переключаются обратно по истечении таймера? Подозреваю, что проблема в функции handleDisabled. Я поставил туда debugger и увидел, что item в функции handleDisabled undefined. Что я делаю не так и как это исправить?

Вот мой код со всеми необходимыми данными:

const CurrentEventsItem = () => {
    const [timeLeft, setTimeLeft] = useState( 15);
  
    const getPadTime = (time) => time.toString().padStart(2, "0");
  
    const minutes = getPadTime(Math.floor(timeLeft / 60));
    const seconds = getPadTime(timeLeft - minutes * 60);

    useEffect(() => {
        const interval = setInterval(() => {
          setTimeLeft((timeLeft) =>
            timeLeft >= 1 ? timeLeft - 1 : handleDisabled() || 15
          );
        }, 1000);
        return () => clearInterval(interval);
      }, []);
    
      const getRandomElements = (array, count) => {
        const shuffled = array.sort(() => 0.5 - Math.random());
        return shuffled.slice(0, count);
      };

    const [appState, changeState] = useState( [])

    useEffect(() => {
        getCurrentEvents()
      }, [])
    
      const getCurrentEvents = async() => {
        const data = await ProductDataService.getAllCurrentEvents()
        changeState(data.docs.map((doc) => ({...doc.data(), id: doc.id})))
      }

    const [selectedItems, setSelectedItems] = useState(() =>
        getRandomElements(appState, 3)
    );

    useEffect(() => {
        if (timeLeft === 0) {
          const elements = getRandomElements(appState, 3);
          setSelectedItems(elements);
        }
      }, [timeLeft, appState]);

    const toggleActiveStyles = (index) => {
        if (appState[index].statusItem) {
          return "current__events__hot-price disabled";
        } else {
          return "current__events__hot-price";
        }
      };
    
      const toggleActiveStylesBtns = (index) => {
        if (appState[index].statusItem) {
          return "current__events__btn-green disabled";
        } else {
          return "current__events__btn-green";
        }
      };
    
    const toggleActive = (item) => {
     item.statusItem = !item.statusItem;
     changeState([...appState]);
     setSelectedItems([...selectedItems]);
    };

    const handleDisabled = () => {
        appState.forEach((item) => {
          if (item.statusItem) {
            toggleActive(item);
          }
        });
    };

    return (
        <>
        <div className='current__events__wrapper'>
            {selectedItems.map((item, index) => 
                <div className="current__events__hot-price__item" key={index}>
                    <div className={toggleActiveStyles(index)}>
                        <h5 className="current__events__card-title__large">Hot Price</h5>
                    </div>
                    <div className="current__events__image">
                        <img src={item.avatar} alt='user' className="rounded-circle" width='75' height='75'/>
                    </div>
                    <div className="current__events__info">
                        <h4 className="current__events__title__middle">{item.title}</h4>
                    </div>
                    
                    <div className="current__events__timer">
                        <span>{minutes}</span>
                        <span>:</span>
                        <span>{seconds}</span>
                    </div>

                    <button className={toggleActiveStylesBtns(index)} onClick={() => toggleActive(item)} disabled={item.statusItem}>СДЕЛАТЬ ХОД</button> 
                </div> 
            )}

            
            </div>
        </>
    )
}

Ответы (1 шт):

Автор решения: EzioMercer

Если честно, долго пытался именно менять код, но устал и просто практически переписал с нуля пошагово, следуя поставленным вами условиям. Пытался писать близко к вашему стилю. Одна вещь очень сильно путала - это то, что на true вы кнопки отключаете.

Вот что получилось:

App.js
import { useState, useEffect } from 'react';
import './styles.css';
import CurrentEventsItem from './CurrentEventsItem';
import ProductDataService from './productServices';

export default function App() {
  const [items, setItems] = useState([]);

  useEffect(() => async () => await getCurrentEvents(), []);

  const getCurrentEvents = async () => {
    const data = await ProductDataService.getAllCurrentEvents();

    setItems(data.docs.map((doc) => ({ ...doc.data(), id: doc.id })));
  };

  return <CurrentEventsItem items={items} />;
}
CurrentEventsItem.js
import { useState, useEffect } from "react";

const getRandomElements = (array, count) => {
  const shuffled = array.sort(() => 0.5 - Math.random());
  return shuffled.slice(0, count);
};

const getPadTime = (time) => time.toString().padStart(2, "0");

const CurrentEventsItem = ({items}) => {
  const [timeLeft, setTimeLeft] = useState(5);
  const [appState, changeState] = useState(items);
  const [selectedItems, setSelectedItems] = useState(getRandomElements(appState, 3));

  const minutes = getPadTime(Math.floor(timeLeft / 60));
  const seconds = getPadTime(timeLeft - minutes * 60);

  useEffect(() => {
    changeState(items);
    setSelectedItems(getRandomElements(items, 3));
  }, [items])

  useEffect(() => {
    const interval = setInterval(() => {
      setTimeLeft((timeLeft) => {
        if (timeLeft > 0) return timeLeft - 1;

        enableAll();
        setSelectedItems(getRandomElements(appState, 3));

        return 5;
      });
    }, 1000);

    return () => clearInterval(interval);
  }, [appState]);

  const toggleActiveStyles = (index) => `current__events__hot-price${
    appState[index].statusItem ? ' disabled' : ''
  }`;

  const toggleActiveStylesBtns = (index) => `current__events__btn-green${
    appState[index].statusItem ? ' disabled' : ''
  }`;

  const toggleActive = (item, activate = true) => {
    item.statusItem = activate;
  };

  const handleClick = (item) => {
    toggleActive(item);
    setSelectedItems([...selectedItems]);
  }

  const enableAll = () => {
    selectedItems.forEach(item => toggleActive(item, false));
    setSelectedItems([...selectedItems]);
  }

  return (
    <div className="current__events__wrapper">
        {selectedItems.map((item, index) => (
          <div className="current__events__hot-price__item" key={index}>
            <div className={toggleActiveStyles(index)}>
              <h5 className="current__events__card-title__large">Hot Price</h5>
            </div>
            <div className="current__events__image">
              <img
                src={item.avatar}
                alt="user"
                className="rounded-circle"
                width="75"
                height="75"
              />
            </div>
            <div className="current__events__info">
              <h4 className="current__events__title__middle">{item.title}</h4>
            </div>

            <div className="current__events__timer">
              <span>{minutes}</span>
              <span>:</span>
              <span>{seconds}</span>
            </div>

            <button
              className={toggleActiveStylesBtns(index)}
              onClick={() => handleClick(item)}
              disabled={item.statusItem}
            >
              СДЕЛАТЬ ХОД
            </button>
          </div>
        ))}
      </div>
  );
};

export default CurrentEventsItem;

Если будут вопросы, то задавайте, объясню что и почему написал

→ Ссылка