【问题标题】:React.js, wait for setState to finish before triggering a function?React.js,在触发函数之前等待 setState 完成?
【发布时间】:2016-09-20 23:05:46
【问题描述】:

这是我的情况:

  • 在 this.handleFormSubmit() 上我正在执行 this.setState()
  • 在 this.handleFormSubmit() 内部,我调用 this.findRoutes(); - 这取决于 this.setState() 的成功完成
  • this.setState();在 this.findRoutes 被调用之前没有完成...
  • 如何在调用 this.findRoutes() 之前等待 this.handleFormSubmit() 内部的 this.setState() 完成?

一个低于标准的解决方案:

  • 将 this.findRoutes() 放入 componentDidUpdate()
  • 这是不可接受的,因为会有更多与 findRoutes() 函数无关的状态更改。我不想在更新无关状态时触发 findRoutes() 函数。

请看下面的代码sn-p:

handleFormSubmit: function(input){
                // Form Input
                this.setState({
                    originId: input.originId,
                    destinationId: input.destinationId,
                    radius: input.radius,
                    search: input.search
                })
                this.findRoutes();
            },
            handleMapRender: function(map){
                // Intialized Google Map
                directionsDisplay = new google.maps.DirectionsRenderer();
                directionsService = new google.maps.DirectionsService();
                this.setState({map: map});
                placesService = new google.maps.places.PlacesService(map);
                directionsDisplay.setMap(map);
            },
            findRoutes: function(){
                var me = this;
                if (!this.state.originId || !this.state.destinationId) {
                    alert("findRoutes!");
                    return;
                }
                var p1 = new Promise(function(resolve, reject) {
                    directionsService.route({
                        origin: {'placeId': me.state.originId},
                        destination: {'placeId': me.state.destinationId},
                        travelMode: me.state.travelMode
                    }, function(response, status){
                        if (status === google.maps.DirectionsStatus.OK) {
                            // me.response = response;
                            directionsDisplay.setDirections(response);
                            resolve(response);
                        } else {
                            window.alert('Directions config failed due to ' + status);
                        }
                    });
                });
                return p1
            },
            render: function() {
                return (
                    <div className="MapControl">
                        <h1>Search</h1>
                        <MapForm
                            onFormSubmit={this.handleFormSubmit}
                            map={this.state.map}/>
                        <GMap
                            setMapState={this.handleMapRender}
                            originId= {this.state.originId}
                            destinationId= {this.state.destinationId}
                            radius= {this.state.radius}
                            search= {this.state.search}/>
                    </div>
                );
            }
        });

【问题讨论】:

    标签: javascript reactjs state


    【解决方案1】:

    setState() 有一个可选的回调参数,您可以使用它。你只需要稍微改变你的代码,如下:

    // Form Input
    this.setState(
      {
        originId: input.originId,
        destinationId: input.destinationId,
        radius: input.radius,
        search: input.search
      },
      this.findRoutes         // here is where you put the callback
    );
    

    注意对findRoutes 的调用现在在setState() 调用中, 作为第二个参数。
    没有(),因为您正在传递函数。

    【讨论】:

    • 这对于在 ReactNative 中的 setState 之后重置 AnimatedValue 非常有效。
    • 通用版本this.setState({ name: "myname" }, function() { console.log("setState completed", this.state) })
    • 您似乎不能向 setState 传递多个回调。是否有一种不混乱的链接回调方式?可以说我有 3 个方法都需要运行,并且都更新状态。处理此问题的首选方法是什么?
    • 如果没有更多信息,我认为 1 回调将是一个容器,它调用您的 3 个方法中的任何一个(如果它们需要按顺序触发)。或者容器依次调用您的 3 个方法,然后执行一个 setState()(如果您真的不需要连续进行 4 个状态更改)。您能否详细说明一下我们的具体案例?
    • 为我工作...非常感谢。
    【解决方案2】:
           this.setState(
            {
                originId: input.originId,
                destinationId: input.destinationId,
                radius: input.radius,
                search: input.search
            },
            function() { console.log("setState completed", this.state) }
           )
    

    这可能会有所帮助

    【讨论】:

      【解决方案3】:

      如果有人在这里登陆并使用钩子遇到相同的情况,则可以通过以下过程实现相同的行为

      const [data, setData] = useState(false);
      
      useEffect(() => {
          doSomething(); // This is be executed when the state changes
      }, [data]);
      
      setdata(true);
      

      这里useEffect会在数据发生任何变化后运行,我们可以执行任何依赖的任务。

      【讨论】:

        【解决方案4】:

        setState 采用新的状态和可选的回调函数,在状态更新后调用。

        this.setState(
          {newState: 'whatever'},
          () => {/*do something after the state has been updated*/}
        )
        

        【讨论】:

          【解决方案5】:

          根据setState() 的文档,新状态可能不会反映在回调函数findRoutes() 中。这是React docs的摘录:

          setState() 不会立即改变 this.state 而是创建一个挂起的状态转换。调用此方法后访问 this.state 可能会返回现有值。

          不保证 setState 调用的同步操作,调用可能会被批处理以提高性能。

          所以这是我建议你应该做的。您应该在回调函数findRoutes() 中传递新状态input

          handleFormSubmit: function(input){
              // Form Input
              this.setState({
                  originId: input.originId,
                  destinationId: input.destinationId,
                  radius: input.radius,
                  search: input.search
              });
              this.findRoutes(input);    // Pass the input here
          }
          

          findRoutes() 函数应该这样定义:

          findRoutes: function(me = this.state) {    // This will accept the input if passed otherwise use this.state
              if (!me.originId || !me.destinationId) {
                  alert("findRoutes!");
                  return;
              }
              var p1 = new Promise(function(resolve, reject) {
                  directionsService.route({
                      origin: {'placeId': me.originId},
                      destination: {'placeId': me.destinationId},
                      travelMode: me.travelMode
                  }, function(response, status){
                      if (status === google.maps.DirectionsStatus.OK) {
                          // me.response = response;
                          directionsDisplay.setDirections(response);
                          resolve(response);
                      } else {
                          window.alert('Directions config failed due to ' + status);
                      }
                  });
              });
              return p1
          }
          

          【讨论】:

          • 这有一个严重的缺陷 - 将文字 obj 传递给 setState() 因为新状态不好,因为它会导致竞争条件
          • 这是来自 react docs 的另一个引用(自您发布答案以来可能已经更新):“...使用 componentDidUpdate 或 setState 回调(setState(updater, callback)),其中任何一个保证在应用更新后触发”。这对我来说,新状态最肯定地反映在回调函数中。
          【解决方案6】:

          为什么不多一个答案? setState()setState() 触发的render() 在您调用componentDidMount()(第一次执行render())和/或componentDidUpdate()(执行render() 之后的任何时间)时都已完成执行。 (链接是 ReactJS.org 文档。)

          componentDidUpdate() 为例

          调用者,设置引用和设置状态...

          <Cmp ref={(inst) => {this.parent=inst}}>;
          this.parent.setState({'data':'hello!'});
          

          渲染父级...

          componentDidMount() {           // componentDidMount() gets called after first state set
              console.log(this.state.data);   // output: "hello!"
          }
          componentDidUpdate() {          // componentDidUpdate() gets called after all other states set
              console.log(this.state.data);   // output: "hello!"
          }
          

          componentDidMount() 为例

          调用者,设置引用和设置状态...

          <Cmp ref={(inst) => {this.parent=inst}}>
          this.parent.setState({'data':'hello!'});
          

          渲染父级...

          render() {              // render() gets called anytime setState() is called
              return (
                  <ChildComponent
                      state={this.state}
                  />
              );
          }
          

          父级重新渲染子级后,查看componentDidUpdate()中的状态。

          componentDidMount() {           // componentDidMount() gets called anytime setState()/render() finish
          console.log(this.props.state.data); // output: "hello!"
          }
          

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2021-05-17
            • 2022-12-03
            • 2021-12-02
            • 1970-01-01
            相关资源
            最近更新 更多