Переменная не меняет значение React

Делаю для приложение перемотки слайдов, возникла проблема, что при установке значения хука, значение не меняется.

import React from 'react';

function Slides({slides}) {
    const [index, setIndex] = React.useState(0);
    const [slide, setSlide] = React.useState(slides[0]);
     const [disabledButtonRestartAndPrev, SetDisabledButtonRestartAndPrev] = React.useState(true);
    const [disabledButtonNext, setDisabledButtonNext] = React.useState(false);

    const onCLickNext = () => {
        setIndex(index + 1);
        chekEnabled();
        setSlide(slides[index]);
    }

    const onCLickPrev = () => {
        setIndex(index - 1);
        chekEnabled();
        setSlide(slides[index]);
    }

    const onCLickRestart = () => {
        setIndex(0);
        setSlide(slides[0]);
        SetDisabledButtonRestartAndPrev(true);
        setDisabledButtonNext(false);
    }

    const chekEnabled = () => {
        index === slides.length - 1 ? setDisabledButtonNext(true) : setDisabledButtonNext(false);
        index > 0 ? SetDisabledButtonRestartAndPrev(false) : SetDisabledButtonRestartAndPrev(true)
    }
    return (
        <div>
            <div id="navigation" className="text-center">
                <button onClick={onCLickRestart} disabled={disabledButtonRestart} data-testid="button-restart"
                        className="small outlined">Restart
                </button>
                <button onClick={onCLickPrev} disabled={disabledButtonPrevious} data-testid="button-prev"
                        className="small">Prev
                </button>
                <button onClick={onCLickNext} disabled={disabledButtonNext} data-testid="button-next"
                        className="small">Next
                </button>
            </div>
            <div id="slide" className="card text-center">
                <h1 data-testid="title">{slide.title}</h1>
                <p data-testid="text">{slide.text}</p>
            </div>
        </div>
    );
}

export default Slides;

При отладке в браузере при первом нажати на кнопку "Next" index так и остается = 0, при повторном нажатии уже 1, и тд. Подскажите в чем проблема?


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

Автор решения: Nikolya Shirshov

Это связано с несколькими setState подряд, т.к. React группирует их и только потом ререндерит компонент, а состояния обновляются только после ререндера. Пример:

const onClickNext = () => {
    setIndex(index + 1); // Сделали запрос на обновление index
    chekEnabled();
    setSlide(slides[index]); // Но т.к. компонент не ререндерился в index по прежнему 0
}

Решение 1

Можно slide и index объеденить в одно состояние, т.к. они всегда изменяются вместе. Пример:

const [state, setState] = React.useState({ index: 0, slide: slides[0] });

Решение 2 (useEffect)

import React from 'react';

function Slides({ slides }) {
  const [index, setIndex] = React.useState(0);
  const [slide, setSlide] = React.useState(slides[0]);

  const onCLickNext = () => {
    setIndex(index + 1);
  };

  const onCLickPrev = () => {
    setIndex(index - 1);
  };

  const onCLickRestart = () => {
    setIndex(0);
  };

  /** При изменении index вызывается эффект изменения слайда */
  React.useEffect(() => {
    setSlide(slides[index]);
  }, [index]);

  /** Если слайдов стало меньше, а index равен последнему элементу  */
  React.useEffect(() => {
    const maxIndex = slides.length - 1;
    if (index > maxIndex) {
      setIndex(maxIndex);
    }
  }, [slides]);

  return (
    <div>
      <div id="navigation" className="text-center">
        <button
          onClick={onCLickRestart}
          disabled={index === 0} // Нет смысла в состояниях для кнопок в данном примере, т.к. они зависят только от index и slides
          data-testid="button-restart"
          className="small outlined"
        >
          Restart
        </button>
        <button
          onClick={onCLickPrev}
          disabled={index === 0}
          data-testid="button-prev"
          className="small"
        >
          Prev
        </button>
        <button
          onClick={onCLickNext}
          disabled={index >= (slides.length - 1)}
          data-testid="button-next"
          className="small"
        >
          Next
        </button>
      </div>
      <div id="slide" className="card text-center">
        <h1 data-testid="title">{slide.title}</h1>
        <p data-testid="text">{slide.text}</p>
      </div>
    </div>
  );
}

export default Slides;
→ Ссылка