Reputation: 141
I have this function in react to fetch some data from my backed and re-renders the sate. There is some processing going on in the backend so it takes some seconds to render the state.
Is there a way to render a loading message before the state renders?
handleSpecies(e){
console.log(e.target.text)
let filteredSpecies = [];
fetch('http://127.0.0.1:8000/api/films/search/'+ e.target.text).then(results => {
if(results.ok){
return results.json();
}
throw new Error('Failed to fetch');
})
.then(data => {
filteredSpecies = data;
this.setState({species:filteredSpecies})
})
.catch((error) => {
this.setState({ error: error.message } )
});
}
render() {
return (
<div className="col-md-4">
<div className="list-group">
{
this.state.species.map((species, i) => {
return (
<a href="#" className="list-group-item" key={i}>{species.name}</a>
);
})
}
</div>
</div>
)
}
Upvotes: 0
Views: 428
Reputation: 6450
The main thing to remember is that having an is<SomeThing>Loading
flag allows you to A) Have multiple loading indicators for each section of a page, and B) Allows explicit handling of errors, rendering, etc. It helps avoid all of the "well if the list is empty, maybe my API call didn't succeed" or other ambiguous scenarios.
Given only your code, here's what I would do
handleSpecies(e){
this.setState({ isSpeciesLoading : true })
fetch('http://127.0.0.1:8000/api/films/search/'+ e.target.text).then(results => {
if(results.ok){
return results.json();
}
throw new Error('Failed to fetch');
})
.then(data => {
this.setState({species:data, isSpeciesLoading : false})
})
.catch((error) => {
this.setState({ error: error.message, isSpeciesLoading : false } )
});
}
render() {
return (
<div className="col-md-4">
<div className="list-group">
{this.state.isSpeciesLoading ? <div>Loading...</div>
{
!this.state.isSpeciesLoading &&
this.state.species.map((species, i) => {
return (
<a href="#" className="list-group-item" key={i}>{species.name}</a>
);
})
}
</div>
</div>
)
}
Upvotes: 0
Reputation: 929
Defining an isLoading
state variable would be a nice solution for your problem.
You can set it to true
just before firing the async request and set it to false
when request is completed or failed.
Then you just have to conditional render your component based on the isLoading
flag.
A code snippet demonstrating your problem is the following:
class MyComponent extends Component {
state = {
isLoading: false,
species: '',
}
handleSpecies(e) {
console.log(e.target.text)
this.setState({isLoading: true});
let filteredSpecies = [];
const url = 'http://127.0.0.1:8000/api/films/search/'+ e.target.text;
fetch(url)
.then(results => {
if(results.ok){
return results.json();
}
throw new Error('Failed to fetch');
})
.then(data => {
this.setState({
species: data,
isLoading: false,
});
})
.catch((error) => {
this.setState({
error: error.message,
isLoading: false,
});
});
}
render() {
const {
isLoading,
species,
} = this.state;
return (
<div className="col-md-4">
<div className="list-group">
{
isLoading
? <Loading/>
: species.map((specie, i) => <a href="#" className="list-group-item" key={i}> {specie.name} </a>)
}
</div>
</div>
)
}
}
Upvotes: 1
Reputation: 1114
Just add a ternary before maping the species: if species is empty or falsable, then print a message.
<div className="col-md-4">
<div className="list-group">
{(!this.state.species || !this.state.species.length) ?
this.state.species.map((species, i) => {
return (
<a href="#" className="list-group-item" key={i}>{species.name}</a>
);
}) : <p> Please wait </p>
}
</div>
</div>
Upvotes: 2