简体   繁体   English

如何在 React 中使用 async/await 等待数据完成获取

[英]How to wait for data to finish fetching with async/await in React

My own answer to the question works, Pedrag's comment which is included in my answer also works我自己对这个问题的回答有效,我的回答中包含的 Pedrag 的评论也有效

I am doing a http request call and don't want to display the page until the http response came back (the request call is getRoadmaps).我正在执行 http 请求调用,并且在 http 响应返回之前不想显示该页面(请求调用是 getRoadmaps)。 I have a redux state called "loading" to determine if the response came back yet?我有一个称为“加载”的 redux 状态来确定响应是否回来了? Any insights on how to implement that in my code?关于如何在我的代码中实现它的任何见解? My other redux states are roadmap, which contains an object also called roadmap (sorry for the confusion).我的其他 redux 状态是路线图,其中包含一个也称为路线图的对象(抱歉造成混淆)。

PS I saw a few answers that said to do (psuedo code): if not loaded display loading screen else load the normal screen in render. PS我看到一些答案说要做(伪代码):如果没有加载显示加载屏幕否则在渲染中加载正常屏幕。 But my problem doesn't exists in render, but in componentWillMount() where I do但是我的问题不存在于渲染中,而是在我做的 componentWillMount() 中

this.props.getRoadmaps() 

and then 3 lines later where I do然后在我做的 3 行之后

console.log(this.displayRoadmap[0].roadmap[0])

which should successfully log the roadmap if getRoadmaps() finished, it seems like getRoadmaps() is called but then the program continues without getRoadmaps() completely finishes, which causes my displayRoadmap to be undefined.如果 getRoadmaps() 完成,应该成功记录路线图,似乎调用了 getRoadmaps() 但随后程序继续运行而 getRoadmaps() 没有完全完成,这导致我的 displayRoadmap 未定义。 This also leads to some weird phenomenon like if I go from one screen into this component it always works but if I refresh the page it doesn't work这也会导致一些奇怪的现象,比如如果我从一个屏幕进入这个组件它总是有效但如果我刷新页面它就不起作用

import React, { Component } from "react";
import Tree from "./Tree";
import TreeSidebar from "./TreeSidebar";
import { withRouter } from "react-router-dom";
import { connect } from "react-redux";
import { getRoadmaps } from "../actions/roadmapAction";
class OneRoadmap extends Component {
  constructor(props) {
    super(props);
    this.state = {
      loading: this.props.roadmap.loading,
      roadmapName: "click a white circle to show more!",
      roadmapImg:
        "https://cdn3.iconfinder.com/data/icons/harmonicons-06/64/plus-circle-512.png",
      roadmapDetail: [{ text: "Click on something" }, { text: "hi" }],
      treeData: //deleted,just the default big json object
   };
  }

  componentWillMount() {
    this.props.getRoadmaps();

    var location1 = this.props.location.pathname;
    var n = location1.slice(9);

    var current_roadmaps = this.props.roadmap.roadmaps;

    this.displayRoadmap = current_roadmaps.filter(
      eachRoadmap => eachRoadmap._id == n
    );
    // now we have a roadmap
    console.log(this.displayRoadmap[0].roadmap[0]);
    this.setState({ treeData: this.displayRoadmap[0].roadmap[0] });
    console.log("is good");
    console.log(this.props.loading);
  }

  componentDidMount() {}

  handle_on_click_change = d => {
    this.setState({ roadmapName: d.data.name });
    this.setState({ roadmapImg: d.data.img });
    if (d.data.details == undefined) {
      this.setState({
        roadmapDetail: [{ text: "The author did not put anything here" }]
      });
    } else {
      this.setState({ roadmapDetail: d.data.details });
    }
  };

  render() {
    return (
      <div>
        {console.log(this.state.loading)}
        <ul>
          <li styles={{ float: "left" }}>
            <div>
              {console.log(this.state.treeData)}
              <Tree
                on_click_change={this.handle_on_click_change}
                roadmapData={this.state.treeData}
              />
            </div>
          </li>
          <li styles={{ float: "right" }}>
            <div>
              <TreeSidebar
                displayName={this.state.roadmapName}
                displayImg={this.state.roadmapImg}
                displayDetail={this.state.roadmapDetail}
              />
            </div>
          </li>
        </ul>
      </div>
    );
  }
}

const mapStateToProps = state => ({
  roadmap: state.roadmap
});

export default connect(
  mapStateToProps,
  { getRoadmaps }
)(OneRoadmap);

Personally, how I do this I use CSS styles I don't know if this if the best approach to do this but it works for me, I will do something like this就我个人而言,我如何做到这一点我使用 CSS 样式我不知道这是否是最好的方法,但它对我有用,我会做这样的事情

 this state = {
      loaderStyle: 'block',
      contentStyle: 'none'
 }
componentDidMount() {
    If(this.displayRoadmap[0].length > 0) {
       this.setsState({loaderStyle: 'none', contentStyle: 
          'block:
    } else /// do something

};

In my render function, I will do this在我的渲染函数中,我会这样做

 <Loader style={{display: this.state.loaderStyle}}/>

 <Content style={{display: this.state.contentStyle}}>. 
 </Content>

First and most important, don't use componentWillMount , rather, use componentDidMount .首先也是最重要的,不要使用componentWillMount ,而是使用componentDidMount UNSAFE_componentWillMount() is legacy and will be removed in version 17. Now, onto the next issue, you want to start your page off by setting the loading state to true. UNSAFE_componentWillMount()是旧版本,将在版本 17 中删除。现在,在下一个问题上,您希望通过将loading状态设置为 true 来启动页面。

this.state = {
      loading: true,
      roadmapName: "click a white circle to show more!",
      roadmapImg:
        "https://cdn3.iconfinder.com/data/icons/harmonicons-06/64/plus-circle-512.png",
      roadmapDetail: [{ text: "Click on something" }, { text: "hi" }],
      treeData: //deleted,just the default big json object
   };

After that, you want to tweak your render method a bit to support conditional rendering:之后,您需要稍微调整渲染方法以支持条件渲染:

render() {
        if (this.state.loading) {
            return <LoadingBar />;
        }
        return (
            <div>
                <ul>
                    <li styles={{ float: 'left' }}>
                        <div>
                            {console.log(this.state.treeData)}
                            <Tree
                                on_click_change={this.handle_on_click_change}
                                roadmapData={this.state.treeData}
                            />
                        </div>
                    </li>
                    <li styles={{ float: 'right' }}>
                        <div>
                            <TreeSidebar
                                displayName={this.state.roadmapName}
                                displayImg={this.state.roadmapImg}
                                displayDetail={this.state.roadmapDetail}
                            />
                        </div>
                    </li>
                </ul>
            </div>
        );
    }

Now, whenever your data is ready, you can simply do this.setState({ loading: false }) , and the render method will return whatever is not the loading bar.现在,只要您的数据准备就绪,您就可以简单地执行this.setState({ loading: false }) ,并且 render 方法将返回不是加载栏的任何内容。 So in your specific case:所以在你的具体情况下:

this.setState({ treeData: this.displayRoadmap[0].roadmap[0], loading: false });

This can easily achieved by React Higher Order Components as follows.这可以通过 React 高阶组件轻松实现,如下所示。

withLoadingScreen.js withLoadingScreen.js

import * as React from "react";

const withLoadingScreen = WrappedComponent => {
  return class LoadingScreen extends React.Component {
    render() {
      if (this.props.loading) return <div className="pre-loader" > Loading... </div>
      return <WrappedComponent {...this.props} />;
    }
  };
};

export default withLoadingScreen;

Use HOC as follows with your component对您的组件按如下方式使用 HOC

export default compose(
  connect(mapStateToProps, mapActionCreators),
  withLoadingScreen
)(OneRoadmap)

You can pass custom parameters too to the HOC.您也可以将自定义参数传递给 HOC。 For more example please follow links below.有关更多示例,请访问以下链接。 Make changes as you want on your code by referring them.通过引用它们来根据需要对代码进行更改。

https://medium.com/@peterpme/learning-higher-order-components-in-react-by-building-a-loading-screen-9f705b89f569 https://medium.com/@peterpme/learning-higher-order-components-in-react-by-building-a-loading-screen-9f705b89f569

https://medium.com/@Farzad_YZ/handle-loadings-in-react-by-using-higher-order-components-2ee8de9c3deb https://medium.com/@Farzad_YZ/handle-loadings-in-react-by-using-higher-order-components-2ee8de9c3deb

I checked elraphty's answer because he does answer part of my question and he also wanted me to check his answer.我检查了 elraphty 的回答,因为他确实回答了我的部分问题,而且他还希望我检查他的回答。 Anyways, if you have trouble fetching data and waiting for the data to finish fetching , here's a complete example of how I do it.无论如何,如果您在获取数据并等待数据完成获取时遇到问题,这里有一个完整的示例,说明我是如何做到的。 Predrag Beocanin's comment works: Predrag Beocanin 的评论作品:

fetch('some_url', { method: 'GET' }).then(res = res.json()).then(data => { // some parse here; return parsed}

Here's how I do it (using axios instead of metch, sending request to another completely different file /api/roadmap, and then using Redux to update the store.这是我的操作方法(使用 axios 而不是 mech,将请求发送到另一个完全不同的文件 /api/roadmap,然后使用 Redux 更新商店。

In OneRoadmap.js, which is the component I need data fetching in, this is what I call to fetch the data:在 OneRoadmap.js 中,这是我需要获取数据的组件,我调用它来获取数据:

  async componentDidMount() {
await this.props.getRoadmaps();
.....
}

This makes it so that we will run this.props.getRoadmaps() and wait until the line to complete before going to the next line.这使得我们将运行 this.props.getRoadmaps() 并等到该行完成后再转到下一行。 If you don't put async/await, this.props.getRoadmaps() will trigger but the code in componentDidMount() will immediately continue without this.props.getRoadmaps() to finish data fetching and then updating the state.如果你不放置 async/await,this.props.getRoadmaps() 会触发,但是 componentDidMount() 中的代码会立即继续,没有 this.props.getRoadmaps() 来完成数据获取然后更新状态。 Now, ofcourse, Predrag's answer should definitely solve this problem, but just in case you like to place data fetching and state updating with redux in different files (like me), keep reading .现在,当然,Predrag 的答案肯定会解决这个问题,但如果您想在不同的文件中使用 redux 进行数据获取和状态更新(比如我),请继续阅读.

this is what getRoadmaps() does, which is in a file called RoadmapActions.js:这就是 getRoadmaps() 所做的,它位于名为 RoadmapActions.js 的文件中:

    export const getRoadmaps = () => dispatch => {
  dispatch(setRoadmapsLoading());
  return axios
    .get("/api/roadmaps")
    .then(res => dispatch({ type: GET_ROADMAPS, payload: res.data }));
};

This says that we will send a get request to "/api/roadmaps", then take the return data from the get request, and then send a dispatch to the redux store, which will update the store state and we will ultimately use it in OneRoadmap.js.这表示我们将向“/api/roadmaps”发送一个get请求,然后从get请求中获取返回数据,然后向redux store发送一个dispatch,它会更新store状态,我们最终将在OneRoadmap.js。 This is what /api/roadmaps get request returns:这是 /api/roadmaps get 请求返回的内容:

router.get("/", (req, res) => {
Roadmap.find()
.sort({ date: -1 })
.then(roadmaps => res.json(roadmaps));
});

Roadmap.find() goes into our mongo Database and retrieves all the roadmap items, then we return that to whereever it was called from. Roadmap.find() 进入我们的 mongo 数据库并检索所有路线图项目,然后我们将其返回到调用它的任何地方。

With that returned data, we send a dispatch to the reducer with that data, this is what GET_ROADMAPS() does, GET_ROADMAPS locates in roadmapReducer.js:使用返回的数据,我们将带有该数据的调度发送到减速器,这就是 GET_ROADMAPS() 所做的,GET_ROADMAPS 位于 roadmapReducer.js 中:

const initialState = {
  roadmaps: [],
  loading: false,
  current_roadmap: ""
};

export default function(state = initialState, action) {


switch (action.type) {
    case GET_ROADMAPS:
      return {
        ...state,
        roadmaps: action.payload,
        loading: false
      };


}

Then, that's it, your redux store state is updated and since I connected OneRoadmap.js with state.roadmap, I can use it freely in the code.然后,就是这样,您的 redux 存储状态已更新,并且由于我将 OneRoadmap.js 与 state.roadmap 连接起来,因此我可以在代码中自由使用它。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM