【问题标题】:How to make setState synchronous in React after axios responseaxios响应后如何在React中使setState同步
【发布时间】:2017-09-10 07:06:02
【问题描述】:

我使用 axios 发出 HTTP 请求,并在其中发出另一个 HTTP 请求,以添加有关我获得的项目的一些详细信息。将它推送到“订单”数组后,我需要设置状态,但它之前已经完成了,所以我无法以正确的方式打印它。 它适用于 SetTimeout,但我想以更专业的方式来做。 我怎样才能做到同步?

fetchOrders(){
    let orders = [];
    let status;
    this.setState({loading:true});
    http.get('/api/amazon/orders')
        .then(response => {
            if (response.status === 200) status = 200;
            orders = response.data;
            orders.map(order => {
                order.SellerSKU = "";
                http.get(`/api/amazon/orders/items/${order.AmazonOrderId}`)
                    .then(res => {
                        order.SellerSKU = res.data[0].SellerSKU;
                    })
                    .catch(error => {
                        console.log(error);
                    })    
            });
            setTimeout( () => {
                this.setState({orders, error: status ? false : true, loading:false})
            }, 1000);
        })
        .catch(error => {
            this.setState({loading:false, error:true});
            console.error(error);
        });
}

【问题讨论】:

    标签: javascript reactjs asynchronous axios setstate


    【解决方案1】:

    您似乎问错了问题,即如何将异步实现为同步,相反,您实际上是在尝试推迟设置状态,直到一切都完成。 (又名XY Problem

    您不需要使 setState 同步 - 您只需要利用一些 Promise 链接和 Promise.all,这将允许您推迟 setState 调用,直到一切都完成。

    简而言之,您应该能够将其调整为您需要的内容,对响应应用更多转换:

    fetchOrders() {
        http.get('/first')
            .then(r => r.data)
            .then(orders => {
                // This wraps the iterable of promises into another promise
                // that doesn't resolve until all the promises in the iterable
                // have finished.
                return Promise.all(orders.map((order) => http.get(`/second/${order.id}`).then(/* transform */))
            })
            .then(transformedOrderPromises => this.setState({ ... }); 
    }
    

    【讨论】:

      【解决方案2】:

      setState 可以带一个回调函数,改变状态后会执行回调。所以您可以setState 并在回调中添加您的第二个 API 调用。

      类似这样的:

      http.get('/api/amazon/orders')
          .then(response => {
              if (response.status === 200) status = 200;
              orders = response.data;
              this.setState({orders, error: status ? false : true, loading:false}, 
                () => {
                  orders.map(order => {
                    order.SellerSKU = "";
                    http.get(`/api/amazon/orders/items/${order.AmazonOrderId}`)
                        .then(res => {
                          order.SellerSKU = res.data[0].SellerSKU;
                        }).catch(error => {
                          console.log(error);
                        })    
              });
          })

      请注意,我只是编辑了一个肮脏的方式,可能需要您进行一些调整才能使其正常工作。

      【讨论】:

      • 它应该可以工作,当我打印订单时一切正常,但它不会在浏览器中显示“SellerSKU”。所以在它完成 map 方法之前,我的东西仍然是 props go to child。
      • @EliLevit:那我认为你必须展示使用res.data[0].SellerSKU的代码块
      【解决方案3】:

      在我的项目中,我伪造了一个同步的setState 以避免很多陷阱,使代码更清晰。这是我的工作:

      class MyComponent extends React.Component {
         // override react's setState
         setState(partialState) {
            const mergedState = { ...this.state, ...partialState };
            this.state = mergedState;
            super.setState(partialState);
         }
      }
      

      说明:在调用真正的setState之前,状态也被设置为this.state,这样在实际更新之前对它的任何引用都是正确的。

      唯一的缺点是您必须从MyComponent 扩展而不是React.Component

      【讨论】:

      • 那...是个非常糟糕的主意。您应该永远在组件中手动设置状态,如果您需要稍后运行函数,请使用 setState 的回调功能
      猜你喜欢
      • 2018-09-06
      • 2021-11-13
      • 2017-11-01
      • 2020-08-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-08-16
      • 1970-01-01
      相关资源
      最近更新 更多