Handling undefined/null properties in components during first render

I set initial state in constructor. You can of course set initial state of component as static value - empty array or object. I think better way is to set it using props. Therefore you can use you component like so <App items={[1,2,3]} /> or <App /> (which takes value of items from defaultProps object because you not pass it as prop).

Example:

import React, { Component } from 'react';
import PropTypes from 'prop-types';

    class App extends Component {
        constructor(props) {
            super(props);
            this.state = {
                items: [], // or items: {...props.items} 
            };



        }
        async componentDidMount() {
            const res = await this.props.getItems();
            this.setState({items: res.data.items})
        }
        render() {
               return <div></div>
           }
};

App.defaultProps = {
items: []
}

I think the best practice is to tell the user that your data is still loading, then populate the fields with the real data. This approach has been advocated in various blog-posts. Robin Wieruch has a great write up on how to fetch data, with a specific example on how to handle loading data and errors and I will go through his example here. This approach is generally done in two parts.

  1. Create an isLoading variable. This is a bolean. We initially set it to false, because nothing is loading, then set it to true when we try to fetch the data, and then back to false once the data is loaded.
  2. We have to tell React what to render given the two isLoading states.

1. Setting the isLoading variable

Since you did not provide any code, I'll just follow Wieruch's example.

import React, { Component } from 'react';

class App extends Component {
  constructor(props) {
    super(props);

    this.state = {
      dataFromApi: null,
    };
  }

  componentDidMount() {
    fetch('https://api.mydomain.com')
      .then(response => response.json())
      .then(data => this.setState({ dataFromApi: data.dataFromApi }));
  }

  ...
}

export default App;

Here we are using the browser's native fetch() api to get the data when the component mounts via the use of componentDidMount(). This should be quite similar to what you are doing now. Given that the fetch() method is asynchronous, the rest of the page will render and the state will be up dated once the data is received.

In order to tell the user that we are waiting for data to load, we simply add isLoading to our state. so the state becomes:

this.state = {
  dataFromApi: null,
  isLoading: false,
};

The state for isLoading is initially false because we haven't called fetch() yet. Right before we call fetch() inside componentDidMount() we set the state of isLoading to true, as such:

this.setState({ isLoading: true });

We then need to add a then() method to our fetch() Promise to set the state of isLoading to false, once the data has finished loading.

.then(data => this.setState({ dataFromAPi: data.dataFromApi, isLoading: false }));

The final code looks like this:

class App extends Component {
  constructor(props) {
    super(props);

    this.state = {
      dataFromApi: [],
      isLoading: false,
    };
  }

  componentDidMount() {
    this.setState({ isLoading: true });

    fetch('https://api.mydomain.com')
      .then(response => response.json())
      .then(data => this.setState({ dataFromApi: data.dataFromApi, isLoading: false }));
  }

  ...
}

export default App;

2. Conditional Rendering

React allows for conditional rendering. We can use a simple if statement in our render() method to render the component based on the state of isLoading.

class App extends Component {
  ...

  render() {
    const { hits, isLoading } = this.state;

    if (isLoading) {
      return <p>Loading ...</p>;
    }

    return (
      <ul>
        {dataFromApi.map(data =>
          <li key={data.objectID}>
            <a href={data.url}>{data.title}</a>
          </li>
        )}
      </ul>
    );
  }
}

Hope this helps.


It Depends. suppose you are fetching books data from server. here is how to do that.

state = {
  books: null,
}

if, your backend api is correctly setup. You will get either empty array for no books or array with some length

componentDidMount(){
    getBooksFromServer().then(res => {
     this.setState({
     books: res.data
   })
  })
 }

Now In Your render method

   render() {
         const { books } = this.state;
         let renderData;
         if(!books) {
          renderData = <Spinner />
      } else
        if(books.length === 0) {
        renderData = <EmptyScreen />
       }
      else {
     renderData = <Books data = { books } />
   }
   
   return renderData;
   }  

If you are using offline data persistence In that case initially you won't have empty array.So This way of handling won't work. To show the spinner you have to keep a variable loader in state. and set it true before calling api and make it false when promise resolves or rejects.

finally read upon to state.

   const {loader} = this.state;
   if(loader) {
   renderData = <Spinner />
}