简体   繁体   中英

React how to fetch a lot of data from an api

I have an api which provides me with some data which I want to display to the screen int the smoothest way. My idea was to fetch data in an async way from the api and render it as soon I receive a response from the api. This is my class right now

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);

Basically, this class fetches the data on the componentDidMount method and starts a for loop for every episode, until it reaches the end, I feel like this is not better way.Any tips?

The problem with this approach is that componentDidMount and render can take a while to execute depending in the number of episode.

The way to do this in the smoother way possible in my opinion is to is: 1)Use virtualisation for rendering the episode data.So if you have a large number of records only what is visible get render.Take a look at this library https://github.com/bvaughn/react-virtualized

2)Use an on demand approach to only fetch the data that the user can see.You can achieve this by paging the data, so as the user scroll through the records new pages are fetched.

You can use redux-saga, it provides easy to use built in functions like "fork", which behaves as a listener and start the fetching process in the background. Other functions or as they call it "effects" are also available. They are really easy to use and perfect for your use case. Fork example

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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