【问题标题】:data updating before waiting for api call react在等待 api 调用响应之前更新数据
【发布时间】:2021-11-18 11:21:24
【问题描述】:

我正在开发一个 react 应用程序,其中我有一个带有滚动条的表格,并且在每次滚动时我都在更新页码并使用更新的页码进行后续 api 调用,但是页码更新速度非常快,以至于超出了限制页码和api返回空数组,导致数据不完整。

这是我的代码:

handleScroll=async ({ scrollTop }) => {
      console.log('hey');
      if (this.props.masterName && this.props.codeSystem) {
        const params = {};
        await this.props.setPageNumber(this.props.page_num + 1);
        params.code_system_category_id = this.props.masterName;
        params.code_systems_id = this.props.codeSystem;
        params.page_num = this.props.page_num;
        if (this.props.entityName) params.entity_name = this.props.entityName;
        if (this.props.status) params.status = this.props.status;
        console.log(params);

        await this.props.fetchCodeSets(params);
      }
    }

这是在每次滚动时都会调用的函数,在每次滚动时,我都使用 await 将页码增加 1,并使用 await 以 this.props.fetchCodeSets 进行 api 调用,这样滚动在完成之前不会超过api调用,但滚动不断被调用,并导致上述错误。

这是我的带有滚动条的表格:

<StyledTable
          height={250}
          width={this.props.width}
          headerHeight={headerHeight}
          rowHeight={rowHeight}
          rowRenderer={this.rowRenderer}
          rowCount={this.props.codeSets.length}
          rowGetter={({ index }) => this.props.codeSets[index]}
          LoadingRow={this.props.LoadingRow}
          overscanRowCount={5}
          tabIndex={-1}
          className='ui very basic small single line striped table'
          columnsList={columns}
          onScroll={() => this.handleScroll('scroll')}
        />

我正在使用react-virtualized 表,可以在此处找到文档: https://github.com/bvaughn/react-virtualized/blob/master/docs/Table.md

任何线索都可以提供帮助!

【问题讨论】:

  • setPageNumber 是什么?可以分享一下代码吗?如果我们在从 API 获取结果后设置页码会发生什么?就像await this.props.fetchCodeSets(params) 声明之后一样。
  • setPageNumber 只会将 redux 状态下的 page_number 增加 1。
  • 如果我们在 API 调用之后实现呢?
  • 还是一样的行为
  • 只是想知道如何控制滚动,等待调用成功后应该触发

标签: javascript reactjs async-await scroll axios


【解决方案1】:

您在每次滚动交互时都加载一个新页面。如果用户向下滚动 5 个像素,是否需要加载整个页面的数据?当向下滚动另外 2 个像素时,然后是另一个页面?不需要。您只需要在到达可用行的末尾时加载页面。

您可以根据onScroll 回调中的scrollTop 位置和rowHeight 变量使用一些数学运算来确定需要加载哪些页面。

但是react-virtualized 包含一个InfiniteLoader 组件可以为您处理这个问题。它将调用loadMoreRows 函数,其中包含您应该加载的行的startIndexstopIndex。它does not 会跟踪已请求哪些行,因此您可能希望自己执行此操作。


在这里,我将 API 响应存储在以索引为键的字典中,本质上是一个稀疏数组,以支持响应无序返回的任何边缘情况。

我们可以通过查看该索引处是否有数据来检查是否加载了行。

loadMoreRows 函数被InfiniteList 组件调用时,我们将加载后续页面。

import { useState } from "react";
import { InfiniteLoader, Table, Column } from "react-virtualized";
import axios from "axios";

const PER_PAGE = 10;

const ROW_HEIGHT = 30;

export default function App() {
  const [postsByIndex, setPostsByIndex] = useState({});
  const [totalPosts, setTotalPosts] = useState(10000);
  const [lastRequestedPage, setLastRequestedPage] = useState(0);

  const loadApiPage = async (pageNumber) => {
    console.log("loading page", pageNumber);
    const startIndex = (pageNumber - 1) * PER_PAGE;
    const response = await axios.get(
      // your API is probably like `/posts/page/${pageNumber}`
      `https://jsonplaceholder.typicode.com/posts?_start=${startIndex}&_end=${
        startIndex + PER_PAGE
      }`
    );
    // This only needs to happen once
    setTotalPosts(parseInt(response.headers["x-total-count"]));
    // Save each post to the correct index
    const posts = response.data;
    const indexedPosts = Object.fromEntries(
      posts.map((post, i) => [startIndex + i, post])
    );
    setPostsByIndex((prevPosts) => ({
      ...prevPosts,
      ...indexedPosts
    }));
  };

  const loadMoreRows = async ({ startIndex, stopIndex }) => {
    // Load pages up to the stopIndex's page. Don't load previously requested.
    const stopPage = Math.floor(stopIndex / PER_PAGE) + 1;
    const pagesToLoad = [];
    for (let i = lastRequestedPage + 1; i <= stopPage; i++) {
      pagesToLoad.push(i);
    }
    setLastRequestedPage(stopPage);
    return Promise.all(pagesToLoad.map(loadApiPage));
  };

  return (
    <InfiniteLoader
      isRowLoaded={(index) => !!postsByIndex[index]}
      loadMoreRows={loadMoreRows}
      rowCount={totalPosts}
      minimumBatchSize={PER_PAGE}
    >
      {({ onRowsRendered, registerChild }) => (
        <Table
          height={500}
          width={300}
          onRowsRendered={onRowsRendered}
          ref={registerChild}
          rowCount={totalPosts}
          rowHeight={ROW_HEIGHT}
          // return empty object if not yet loaded to avoid errors
          rowGetter={({ index }) => postsByIndex[index] || {}}
        >
          <Column label="Title" dataKey="title" width={100} />
          <Column label="Description" dataKey="body" width={200} />
        </Table>
      )}
    </InfiniteLoader>
  );
}

CodeSandbox Link

我使用的占位符 API 采用 startend 索引而不是页码,因此在此示例中从索引到页码再到索引来回是愚蠢的。但我假设您的 API 使用编号页面。

【讨论】:

    猜你喜欢
    • 2020-03-05
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-03-05
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多