【问题标题】:Right place to update a state - React JS更新状态的正确位置 - React JS
【发布时间】:2025-12-28 15:40:17
【问题描述】:

我正在 React JS 中创建一个简单的滑块组件。我有三个按钮RestartPreviousNext。单击上一个按钮将使当前状态减 1,并且会一直持续到当前状态等于 0。此外,下一个按钮将增加状态直到等于 slides.length-1。滑块对我来说很好用,问题是当最后一张幻灯片接近“下一步”按钮时仍然启用,但是当我单击它时,它就会被禁用。预期的行为是在呈现最后一张幻灯片时,应禁用该按钮,并且不需要额外单击。上一个按钮也有同样的问题。

您可以在此处检查行为:https://codesandbox.io/s/react-live-sandbox-krfjk

我确定,更新状态存在一些问题,我可能在错误的地方更新它,这就是我没有想到的原因。

【问题讨论】:

    标签: reactjs setstate react-state


    【解决方案1】:

    单击“上一个”按钮时,您也有相同的行为。导出禁用状态可能比尝试计算它并将其存储在状态中更容易。

    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>
      );
    }
    

    【讨论】:

      【解决方案2】:

      在您的 nextHandler() 中将 setState 更改为:

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

      考虑到您的应用程序的简单性,您不需要 prevState - 您也不需要在 return 中定义每个 state 字段(react 将隐式填充它们作为它们的最后一个值):

      // 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() 应用类似的修复。

      【讨论】:

      • 明白了!这是我所期待的明确答案。
      【解决方案3】:

      您的代码中的问题是您在检查是否应禁用上一个或下一个按钮之前没有更新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 });
              } 
            }
          );
      }
      

      【讨论】: