React - 等待数据从 API 获取

React - await until data fetches from API

我正在尝试获取一些 JSON 数据,并且我已使用 async/await 来暂停执行,直到数据加载准备就绪。但是 React 似乎完全忽略了这一点并尝试渲染组件。我在下面编写了简化代码来进一步测试,setTimeout 正在模仿 API 调用延迟。

console.log('global');
var index ="Data not loaded";

(async function fetchDataAsync(){
  console.log('async start');

  //mimcs loading data from API calls
  let promise = new Promise((resolve, reject) => {
    setTimeout(() => resolve("Data loaded!"), 1000)
  });

  var index = await promise;
  console.log('async end');
  console.log(index);
})();

class App extends React.Component {
  constructor(props){
    super(props);
    this.state={
      randomIndex:0
    }
    console.log('constructor');
  }
  render(){
    console.log('render');
    return (
      <div > 
        <p>{index}</p>
      </div>
    );
  }
}

输出显示数据未加载,控制台显示如下

全球

异步启动

构造函数

渲染

异步结束

数据已加载!

这是 React 应该如何工作还是我在这里遗漏了什么?什么是放置 API 调用的最佳位置,以便它保证在组件安装之前加载数据。 谢谢

问题是异步函数不会阻止 同步 处理该函数出现的代码。我的意思是,您的代码运行顺序是:

  1. 开始 运行 async 函数直到 await
  2. 创建 App class
  3. ...在 App class 之后您还没有显示的任何其他内容;我猜你在某处使用 App...
  4. 顶级代码完成
  5. 计时器触发
  6. 等待计时器结算的承诺
  7. asycn 函数调用创建的 promise 结算

注意 2-4 发生在 5-7 之前。

你想要做的是

  1. 在获得数据之前不要使用 App,或者

  2. App一个"waiting"它在等待数据时呈现的状态

这些解决方案中的每一种都适用于不同的情况,因此您需要选择适合您情况的解决方案。

这不是 React 的做事方式,在渲染之前等待数据到来。

你的代码应该是反应式的,它应该触发数据到达的组件。

这里是你如何做到这一点

class App extends React.Component {
  constructor(props){
    super(props);
    this.state={
      randomIndex:0,
      data: '',
      loading: false
    }
    console.log('constructor');
  }
  
  fetchDataAsync = async () => {
    console.log('async start');
     this.setState({ loading: true });
    //mimcs loading data from API calls
    let promise = new Promise((resolve, reject) => {
      setTimeout(() => resolve("Data loaded!"), 1000)
    });

    var data = await promise;
    console.log('async end');
    console.log(data);
    this.setState({ loading: false, data });
  }

  render(){
    console.log('render');
    const { loading, data } = this.state;
    if(loading) return (<div className="loader">Loading ... </div>);
    if (!loading && data) {
      return (
        <div > 
          <p>{data}</p>
        </div>
      );
    }
    return null;
  }
}