数组完成渲染后反应渲染组件

时间:2018-10-25 12:06:13

标签: reactjs render

我正在componentDidMount上进行api调用,问题是该api需要花费几秒钟来响应,同时用户可以选择转到另一个页面 这样做会发出另一个请求,如果发生这种情况,则应用程序崩溃。如何解决此问题?为了解决此问题,我只需要在所有数组都呈现后才呈现分页组件,该怎么办?

SpannableString ss = new SpannableString("selling audi for 30000 , contact show phone number");
ClickableSpan clickableSpan = new ClickableSpan() {
@Override
public void onClick(View textView) {
    //here you can change the phone number
}
@Override
public void updateDrawState(TextPaint ds) {
    super.updateDrawState(ds);
    ds.setUnderlineText(false);
}
};
ss.setSpan(clickableSpan, 22, 27, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);

TextView textView = (TextView) findViewById(R.id.hello);
textView.setText(ss);
textView.setMovementMethod(LinkMovementMethod.getInstance());
textView.setHighlightColor(Color.TRANSPARENT);

分页组件是带有import React, { Component, Fragment } from "react"; import Episode from "../components/Episode"; import "react-virtualized/styles.css"; // only needs to be imported once import { withStyles } from "@material-ui/core/styles"; import Typography from "@material-ui/core/Typography"; import Grid from "@material-ui/core/Grid"; import Paper from "@material-ui/core/Paper"; import ButtonBase from "@material-ui/core/ButtonBase"; import CircularProgress from "@material-ui/core/CircularProgress"; import Like from "@material-ui/icons/ThumbUp"; import IconButton from "@material-ui/core/IconButton"; import NextButton from "@material-ui/icons/NavigateNext"; import PreviousButton from "@material-ui/icons/NavigateBefore"; // This example assumes you have a way to know/load this information const styles = theme => ({ //styles }); class SeriesPage extends Component { constructor(props) { super(props); this.state = { apiToken: "", serie: { image: "", description: "", title: "", likes: 0, type: "", apiName: "" }, startEpisode: 1, endEpisode: 10, episodes: [], loaded: false, clicked: false, enabled: true }; } componentDidMount() { this.initialize(this.state.startEpisode, this.state.endEpisode); } initialize = async (startIndex, stopIndex) => { await this.getTokenFromApi(); await this.getSerieDetailsByApiName(); await this.getEpisodeBySeriesApiNameWithRange(startIndex, stopIndex); }; getTokenFromApi = async () => { const data = { name: "generateToken", param: { email: "*", pass: "*" } }; return fetch("*", { 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: "*", param: { serieApiName: this.props.match.params.series } }; return fetch("*", { 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, loaded: true }, () => { console.log(this.state); } ); } }) .catch(error => { this.setState({ episodes: "There was an internal error" }); throw error; }); }; getEpisodeBySeriesApiNameWithRange = async (startIndex, stopIndex) => { const data = { name: "*", param: { serieApiName: this.props.match.params.series, startIndex: startIndex, stopIndex: stopIndex } }; return fetch("*", { 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(prevState => ({ episodes: [...prevState.episodes, ...responseData.response.result] })); } }) .catch(error => { this.setState({ episodes: "There was an internal error" }); }); }; handleLikeClick = () => { if (this.state.clicked) { this.setState( prevState => ({ clicked: !prevState.clicked, serie: { ...prevState.serie, likes: Number(prevState.serie.likes) - 1 } }), () => { const data = { name: "removeLikeSerie", param: { serieApiName: this.state.serie.apiName } }; return fetch("*", { 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; }) .catch(error => { this.setState({ episodes: "There was an internal error" }); }); } ); } else { this.setState( prevState => ({ clicked: !prevState.clicked, serie: { ...prevState.serie, likes: Number(prevState.serie.likes) + 1 } }), () => { const data = { name: "likeSerie", param: { serieApiName: this.state.serie.apiName } }; return fetch("*", { 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; }) .catch(error => { this.setState({ episodes: "There was an internal error" }); }); } ); } }; previousPage = () => { if (this.state.startEpisode === 11) { this.setState( prevState => ({ episodes: [], startEpisode: prevState.startEpisode - 10, endEpisode: prevState.endEpisode - 10, enabled: true }), () => { this.initialize(this.state.startEpisode, this.state.endEpisode); } ); } else if (this.state.startEpisode > 10) { this.setState( prevState => ({ episodes: [], startEpisode: prevState.startEpisode - 10, endEpisode: prevState.endEpisode - 10 }), () => { this.initialize(this.state.startEpisode, this.state.endEpisode); } ); } }; nextPage = () => { this.setState( prevState => ({ episodes: [], startEpisode: prevState.startEpisode + 10, endEpisode: prevState.endEpisode + 10, enabled: false }), () => { this.initialize(this.state.startEpisode, this.state.endEpisode); } ); }; renderRow = item => { const { classes, headerIsHidden, ...other } = this.props; return <Episode key={item.videoId} episode={item} {...other} />; }; // Render your list render() { const { classes } = this.props; return ( <Fragment> <div className={classes.serieDetails}> {this.state.loaded ? ( <Paper className={classes.root}> <Grid container spacing={16}> <Grid item> <ButtonBase className={classes.image}> <img className={classes.img} alt={this.state.serie.title + " Image"} src={this.state.serie.image} /> </ButtonBase> </Grid> <Grid item xs={12} sm container> <Grid item xs container direction="column" spacing={16}> <Grid item xs> <Typography gutterBottom variant="subtitle1"> {this.state.serie.title} </Typography> <Typography gutterBottom> {this.state.serie.description} </Typography> <Typography color="textSecondary"> <IconButton className={classes.button} className={this.state.clicked ? classes.liked : ""} aria-label="Like this serie" onClick={this.handleLikeClick} > <Like /> </IconButton> {this.state.serie.likes} </Typography> </Grid> </Grid> </Grid> </Grid> </Paper> ) : ( "" )} </div> <div className={classes.content}> <div className={classes.innerContent}> {this.state.episodes.constructor === String ? ( this.state.episodes ) : ( <div> {this.state.episodes.map(this.renderRow)} <div className={classes.pagination}> <IconButton aria-label="Previous" className={classes.button} onClick={this.previousPage} disabled={this.state.enabled} > <PreviousButton /> </IconButton> <IconButton aria-label="Next" className={classes.button} onClick={this.nextPage} > <NextButton /> </IconButton> </div> </div> )} </div> </div> </Fragment> ); } } export default withStyles(styles, { withTheme: true })(SeriesPage); 的div 数组以状态存储

1 个答案:

答案 0 :(得分:1)

在您的状态中添加一个名为loading的变量,该变量最初为true:

state = {
      ...,
      loading: true
}

在getEpisodeBySeriesApiNameWithRange的响应返回之后,您可以将loadingState设置为false:

 getEpisodeBySeriesApiNameWithRange = async (startIndex, stopIndex) => {
    const data = {
      name: "*",
      param: {
        serieApiName: this.props.match.params.series,
        startIndex: startIndex,
        stopIndex: stopIndex
      }
    };
    return fetch("*", {
      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",
            loading: false
          });
          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(prevState => ({
            episodes: [...prevState.episodes, ...responseData.response.result],
            loading: false
          }));
        }
      })
      .catch(error => {
        this.setState({
          episodes: "There was an internal error",
          loading: false
        });
      });
  };

当您单击nextPage或previousPage setState时,加载状态再次为true:

 nextPage = () => {
this.setState(
     prevState => ({
        episodes: [],
        startEpisode: prevState.startEpisode + 10,
        endEpisode: prevState.endEpisode + 10,
        enabled: false,
        loading: true
      }),
      () => {
        this.initialize(this.state.startEpisode, this.state.endEpisode);
      }
    );
  };

上一页将相同。

render 中,仅当加载为false时才渲染组件,这意味着已获取数据:

{this.state.loading ? null : <div className={classes.content}>
      <div className={classes.innerContent}>
        {this.state.episodes.constructor === String ? (
          this.state.episodes
        ) : (
          <div>
            {this.state.episodes.map(this.renderRow)}
            <div className={classes.pagination}>
              <IconButton
                aria-label="Previous"
                className={classes.button}
                onClick={this.previousPage}
                disabled={this.state.enabled}
              >
                <PreviousButton />
              </IconButton>
              <IconButton
                aria-label="Next"
                className={classes.button}
                onClick={this.nextPage}
              >
                <NextButton />
              </IconButton>
            </div>
          </div>
        )}
      </div>
    </div>
   }