Правильное место для обновления состояния — React JS

Я создаю простой компонент слайдера в React JS. У меня есть три кнопки Restart, Previous, Next. Нажатие на предыдущую уменьшит текущее состояние на 1 и будет продолжаться до тех пор, пока текущее состояние не станет равным 0. Кроме того, кнопка «Далее» будет увеличивать состояние до тех пор, пока оно не станет равным slides.length-1. Ползунок отлично работает для меня, проблема в том, что когда последний слайд приближается к кнопке «Далее», она все еще включена, но когда я нажимаю ее, она становится отключенной. Ожидаемое поведение заключается в том, что при отображении последнего слайда кнопка должна быть отключена, и дополнительный щелчок не требуется. Та же проблема с предыдущей кнопкой.

Вы можете проверить поведение здесь: https://codesandbox.io/s/react-live-sandbox-krfjk

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


person Nadeem Ahmad    schedule 17.06.2020    source источник


Ответы (3)


У вас такое же поведение при нажатии кнопки «предыдущий». Может быть проще получить отключенное состояние, чем пытаться вычислить его и сохранить в состоянии.

Функция рендеринга Slides.js

render() {
  const { slides } = this.props;
  return (
    <div>
      <div id="navigation">
        <button
          data-testid="button-restart"
          onClick={this.resandler}
          data-testid="button-restart">
          Restart
        </button>
        <button
          data-testid="button-prev"
          onClick={this.prevHandler}
          data-testid="button-prev"
          // if current slide is 0, there is no previous
          disabled={this.state.currentSlide === 0}>
          Prev
        </button>
        <button
          data-testid="button-next"
          onClick={this.nextHandler}
          data-testid="button-next"
          // if current slide is slides.length - 1, there is no next
          disabled={this.state.currentSlide === this.props.slides.length - 1}>
          Next
        </button>
      </div>
      {slides.map((slide, i) => {
        return i === this.state.currentSlide ? (
          <div id="slide" key={slide + i}>
            <h1 data-testid="title">{slide.title}</h1>
            <p data-testid="text">{slide.text}</p>
          </div>
        ) : null;
      })}
    </div>
  );
}

Редактировать слайдер цитаты предыдущий/следующий пример

person Drew Reese    schedule 17.06.2020

Внутри вашего nextHandler() измените setState на это:

this.setState({
      currentSlide: this.state.currentSlide + 1,
      nextDisabled: this.state.currentSlide + 1 === this.props.slides.length -  1,
      prevDisabled: false,
});

Учитывая простоту вашего приложения, вам не нужен prevState — вам также не нужно определять каждое поле состояния в возврате (реакция неявно заполнит их как их последнее значение):

// This can be just prevState => since you aren't using props
this.setState((prevState, props) => {
  if (prevState.currentSlide < this.props.slides.length - 1) {
    return {
      currentSlide: prevState.currentSlide + 1,
      prevDisabled: false, 
    };
  } else if (prevState.currentSlide === this.props.slides.length - 1) { // Issue is here
    return {
      nextDisabled: true,
      prevDisabled: false, // Don't need to keep defining this
    };
  }
});

Причина, по которой это не сработало, заключается в том, что вы использовали следующее, чтобы проверить, должна ли кнопка быть отключена:

prevState.currentSlide === this.props.slides.length - 1

prevState будет на 1 отставать от того, что вы намеревались. Для этого вам нужно использовать текущее состояние. Таким образом, другим исправлением было бы изменить его на:

prevState.currentSlide + 1 === this.props.slides.length - 1

Вы можете применить аналогичное исправление к файлу prevHandler().

person Dylan Kerler    schedule 17.06.2020
comment
Попался! Это четкий ответ, которого я ожидал, хотя. - person Nadeem Ahmad; 17.06.2020

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

Когда какая-либо кнопка нажата, сначала обновите currentSlide, а затем проверьте, какая кнопка должна быть включена или отключена.

nextHandler функция должна выглядеть так

nextHandler() {
    this.setState(
        prevState => ({ currentSlide: ++prevState.currentSlide}),
        () => {
          if (this.state.currentSlide < this.props.slides.length - 1) {
            this.setState({ prevDisabled: false });
          } else {
            this.setState({ nextDisabled: true });
          }
        }
    );
} 

и prevHandler должно быть вот так

prevHandler() {
    this.setState(
      prevState => ({ currentSlide: --prevState.currentSlide}),
      () => {
        if (this.state.currentSlide === 0) {
          this.setState({ nextDisabled: false, prevDisabled: true });
        } 
      }
    );
}

Редактировать реактивную живую песочницу

person Yousaf    schedule 17.06.2020