首页 > 解决方案 > React 如何从 api 获取大量数据

问题描述

我有一个 api,它为我提供了一些我想以最流畅的方式显示到屏幕上的数据。我的想法是以异步方式从 api 获取数据,并在收到来自 api 的响应后立即渲染它。这是我现在的课

import React, { Component, Fragment } from "react";   
import Grid from "@material-ui/core/Grid";
import Typography from "@material-ui/core/Typography";
import Episode from "../components/Episode";

import PropTypes from "prop-types";
import { withStyles } from "@material-ui/core/styles";
import CircularProgress from "@material-ui/core/CircularProgress";

const styles = theme => ({
  progress: {
    display: "flex",
    justifyContent: "center",
    alignItems: "center"
  },
  text: {
    marginTop: 20,
    marginLeft: 30
  }
});

class SeriesPage extends Component {
  constructor(props) {
    super(props);

    this.state = {
      apiToken: "",
      serie: null,
      episode: 1,
      episodes: [],
      endEpisode: 100
    };
  }

  async componentDidMount() {
    await this.getTokenFromApi();
    await this.getSerieDetailsByApiName();
    for(int i = 0; i< this.state.endEpisode; i++){
        this.getSerieEpisode();
    }
  }

  getTokenFromApi = async () => {
    const data = {
      name: "generateToken",
      param: {
        email: "-",
        pass: "-"
      }
    };
    return fetch("api", {
      method: "post",
      headers: {
        Accept: "application/json",
        "Content-Type": "application/json"
      },
      body: JSON.stringify(data)
    })
      .then(response => {
        if (!response.ok) {
          this.setState({
            episodes: "Network request failed"
          });
          throw Error("Network request failed");
        }
        return response;
      })
      .then(res => {
        return res.json();
      })
      .then(content => {
        if (content.response.status === 200) {
          this.setState({
            apiToken: content.response.result.token
          });
        }
      })
      .catch(error => {
        this.setState({
          episodes: "There was an internal error"
        });
        throw error;
      });
  };

  getSerieDetailsByApiName = async () => {
    const data = {
      name: "getSerieDetailsByApiName",
      param: {
        serieApiName: this.props.match.params.series
      }
    };
    return fetch("api", {
      method: "post",
      headers: {
        Authorization: "Bearer " + this.state.apiToken,
        "Content-Type": "application/json"
      },
      body: JSON.stringify(data)
    })
      .then(response => {
        if (!response.ok) {
          this.setState({
            episodes: "Network request failed"
          });
          throw Error("Network request failed");
        }
        return response;
      })
      .then(response => {
        return response.json(); //response.json() is resolving its promise. It waits for the body to load
      })
      .then(responseData => {
        if (responseData.response.status === 200) {
          this.setState({
            serie: responseData.response.result
          });
        }
      })
      .catch(error => {
        this.setState({
          episodes: "There was an internal error"
        });
        throw error;
      });
  };

  getSerieEpisode = async () => {
    const data = {
      name: "getEpisodeBySeriesApiName",
      param: {
        serieApiName: this.props.match.params.series,
        ep: this.state.episode
      }
    };
    console.log(data);
    return fetch("api", {
      method: "post",
      headers: {
        Authorization: "Bearer " + this.state.apiToken,
        "Content-Type": "application/json"
      },
      body: JSON.stringify(data)
    })
      .then(response => {
        if (!response.ok) {
          this.setState({
            episodes: "Network request failed"
          });
          throw Error("Network request failed");
        }
        return response;
      })
      .then(response => {
        return response.json(); //response.json() is resolving its promise. It waits for the body to load
      })
      .then(responseData => {
        console.log(responseData);
        if (responseData.response.status === 200) {
          this.setState(prevState => ({
            episodes: [...prevState.episodes, responseData.response.result]
          }));
        }
      })
      .catch(error => {
        this.setState({
          episodes: "There was an internal error"
        });
      });
  };

  render() {
    const { classes, theme } = this.props;
    var series =
      this.state.serie === "No results found" ? (
        <Typography
          className={classes.text}
          component="h2"
          variant="h5"
          color="error"
          gutterBottom
        >
          {this.state.serie}
        </Typography>
      ) : this.state.episodes === "There was an internal error" ? (
        <Typography
          className={classes.text}
          component="h2"
          variant="h5"
          color="error"
          gutterBottom
        >
          {this.state.episodes}
        </Typography>
      ) : (
        this.state.episodes.map((item, i) => {
          const { classes, headerIsHidden, ...other } = this.props;
          return <Episode key={i} episode={item} {...other} />;
        })
      );

    if (this.state.episodes) {
      return (
        <Fragment>
          <div className="series">
            <div className="container">
              <Grid container spacing={24}>
                {series}
                {this.state.loadingState ? (
                  <p className="loading"> loading More Items..</p>
                ) : (
                  ""
                )}
              </Grid>
            </div>
          </div>
        </Fragment>
      );
    } else {
      return (
        <Grid className={classes.progress} item xs={12}>
          <CircularProgress size={100} />
        </Grid>
      );
    }
  }
}
export default withStyles(styles, { withTheme: true })(SeriesPage);

基本上,这个类在 componentDidMount 方法上获取数据并为每一集启动一个 for 循环,直到它到达结尾,我觉得这不是更好的方法。有什么提示吗?

标签: reactjsperformancefetchmaterial-ui

解决方案


这种方法的问题是 componentDidMount 和 render 可能需要一段时间才能执行,具体取决于情节的数量。

在我看来,以更流畅的方式做到这一点的方法是:1)使用虚拟化来渲染剧集数据。所以如果你有大量记录,只有可见的才能渲染。看看这个库 https ://github.com/bvaughn/react-virtualized

2)使用按需方法仅获取用户可以看到的数据。您可以通过分页数据来实现这一点,以便用户滚动浏览记录时获取新页面。


推荐阅读