我正在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
数组以状态存储
答案 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>
}