Danae
Danae

Reputation: 141

react render message before loading new state

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

Answers (3)

Mark C.
Mark C.

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

dmraptis
dmraptis

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

samb102
samb102

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

Related Questions