【问题标题】:setState conditionally in reactsetState 有条件地做出反应
【发布时间】:2019-03-20 16:41:15
【问题描述】:

我知道有些反应,但我陷入了一个奇怪的境地。

我有两个输入和一个按钮, 当两个输入都不为空时,应启用该按钮。 所以,我为每个输入值使用了一个 state 属性,还有一个属性告诉我两个输入是否都有值:

this.state = {
  title: '',
  time :'', 
  enabled : false
}

我也为每个输入设置了一个 onChange 来相应地设置 State:

<input type="text" id="time" name="time" onChange={this.onChange.bind(this)} value={this.state.time}></input>
<input type="text" id="title" name="title" onChange={this.onChange.bind(this)} value={this.state.title}></input>

而onChange是这样的

onChange(e){
    this.setState({
        [e.target.id] : e.target.value,
        enabled : ( (this.state.title==='' || this.state.time==='' ) ? false : true)
      });
  }

问题在于 setState 查看之前的状态,而 enable 总是落后一步,所以如果我在第一个输入 X 并在第二个输入 Y,那么 enabled 仍然是 false。

我设法通过使用 setTimeout 并在其中插入第二行来解决它,但对我来说它看起来不对。

  onChange(e){

    this.setState({
        [e.target.id] : e.target.value,
    });

    setTimeout(() => {
      this.setState({
        enabled : ( (this.state.title==='' || this.state.time==='' ) ? false : true)
    });
    }, 0);

  }

有更好的解决方案吗?

【问题讨论】:

    标签: javascript reactjs setstate


    【解决方案1】:

    有更好的解决方案吗?

    有 3 种解决方案供您实现,只需选择一种


    解决方案 1:使用componentDidUpdate()

    class App extends React.Component {
      constructor(props) {
        super(props);
        this.state = {
          title: "",
          time: "",
          enabled: false
        };
      }
      onChange(e) {
        this.setState({
          [e.target.id]: e.target.value
        });
      }
       componentDidUpdate(prevProps, prevState) {
         if (
           prevState.time !== this.state.time ||
           prevState.title !== this.state.title
         ) {
           if (this.state.title && this.state.time) {
             this.setState({ enabled: true });
           } else {
             this.setState({ enabled: false });
           }
         }
       }
      render() {
        return (
          <React.Fragment>
            <input
              type="text"
              id="time"
              name="time"
              onChange={this.onChange.bind(this)}
              value={this.state.time}
            />
            <input
              type="text"
              id="title"
              name="title"
              onChange={this.onChange.bind(this)}
              value={this.state.title}
            />
            <button disabled={!this.state.enabled}>Button</button>
          </React.Fragment>
        );
      }
    }
    
    const rootElement = document.getElementById("root");
    ReactDOM.render(<App />, rootElement);
    <script crossorigin src="https://unpkg.com/react@16/umd/react.development.js"></script>
    <script crossorigin src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"></script>
    <div id="root"></div>

    解决方案 2:使用 setState 回调

    class App extends React.Component {
      constructor(props) {
        super(props);
        this.state = {
          title: "",
          time: "",
          enabled: false
        };
      }
      onChange(e) {
         this.setState({[e.target.id]: e.target.value},
           () => {
             if (this.state.title && this.state.time) {
               this.setState({ enabled: true });
             } else {
               this.setState({ enabled: false });
             }
           }
         );
      }
      render() {
        return (
          <React.Fragment>
            <input
              type="text"
              id="time"
              name="time"
              onChange={this.onChange.bind(this)}
              value={this.state.time}
            />
            <input
              type="text"
              id="title"
              name="title"
              onChange={this.onChange.bind(this)}
              value={this.state.title}
            />
            <button disabled={!this.state.enabled}>Button</button>
          </React.Fragment>
        );
      }
    }
    
    const rootElement = document.getElementById("root");
    ReactDOM.render(<App />, rootElement);
    <script crossorigin src="https://unpkg.com/react@16/umd/react.development.js"></script>
    <script crossorigin src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"></script>
    <div id="root"></div>

    解决方案 3:根据titletime 计算enabled

    class App extends React.Component {
      constructor(props) {
        super(props);
        this.state = {
          title: "",
          time: ""
        };
      }
      onChange(e) {
        this.setState({
          [e.target.id]: e.target.value
        });
      }
      render() {
        const enabled = this.state.time && this.state.title;
        return (
          <React.Fragment>
            <input
              type="text"
              id="time"
              name="time"
              onChange={this.onChange.bind(this)}
              value={this.state.time}
            />
            <input
              type="text"
              id="title"
              name="title"
              onChange={this.onChange.bind(this)}
              value={this.state.title}
            />
            <button disabled={!enabled}>Button</button>
          </React.Fragment>
        );
      }
    }
    
    const rootElement = document.getElementById("root");
    ReactDOM.render(<App />, rootElement);
    <script crossorigin src="https://unpkg.com/react@16/umd/react.development.js"></script>
    <script crossorigin src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"></script>
    <div id="root"></div>

    【讨论】:

      【解决方案2】:

      首先,启用状态不需要维护,因为它可以从其他状态值派生出来,可以直接在渲染中完成

      onChange(e){
          this.setState({
              [e.target.id] : e.target.value,
          });
      }
      render() {
         const enabled = this.state.title !== "" && this.state.time !== "";
         return (
             <div>
                <input type="text" id="time" name="time" onChange={this.onChange.bind(this)} value={this.state.time}></input>
                <input type="text" id="title" name="title" onChange={this.onChange.bind(this)} value={this.state.title}></input>
                <button disabled={!enabled}>Button</button>
             </div>
         )
      }
      

      【讨论】:

        【解决方案3】:

        @Shubham Khatri 有正确的答案(您实际上并不需要该州的 enabled 属性),但重要的是要了解该州发生了什么以及为什么您使用 setTimeouthack 有效(在我看来很幸运)。

        来自official docs

        setState() 并不总是立即更新组件。有可能 批处理或推迟更新。这使得阅读 this.state 就在调用 setState() 之后,这是一个潜在的陷阱。相反,使用 componentDidUpdate 或 setState 回调(setState(updater, 回调)),其中任何一个都保证在更新后触发 已申请。

        另外,state updates may be asynchronous:

        React 可以将多个 setState() 调用批处理到单个更新中 性能。

        因为 this.props 和 this.state 可能是异步更新的,所以你 不应依赖它们的值来计算下一个状态。

        我希望这可以澄清您的问题。

        【讨论】:

          【解决方案4】:

          如果您想在当前更改发生后设置状态,您可以将第二个参数(回调)传递给 setState,这将在已设置状态并再次设置您的状态后发​​生。它会导致额外的重新渲染,但似乎这就是你要问的。这只是为了让您知道在当前状态更改发生后如何做某事...

          但是,我会同意 Shubham Khatri 的回答。那会更合乎逻辑。

          【讨论】:

            猜你喜欢
            • 2020-05-18
            • 2017-12-25
            • 1970-01-01
            • 2018-03-20
            • 2016-12-23
            • 2018-10-06
            • 2021-06-04
            • 2021-01-11
            • 2019-12-15
            相关资源
            最近更新 更多