最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - Handling undefinednull properties in components during first render - Stack Overflow

programmeradmin0浏览0评论

I'm learning react and it's great, but i've ran into an issue and i'm not sure what the best practice is to solve it.

I'm fetching data from an API in my componentDidMount(), then i'm setting some states with SetState().

Now the problem is that because the first render happens before my states have been set, im sending the initial state values into my components. Right now i'm setting them to empty arrays or empty Objects ({ type: Object, default: () => ({}) }). Then i'm using ternary operator to check the .length or if the property has a value.

Is this the best practice or is there some other way that i'm unaware of?

I would love to get some help with this, so that i do the basics correctly right from the start.

Thanks!

I'm learning react and it's great, but i've ran into an issue and i'm not sure what the best practice is to solve it.

I'm fetching data from an API in my componentDidMount(), then i'm setting some states with SetState().

Now the problem is that because the first render happens before my states have been set, im sending the initial state values into my components. Right now i'm setting them to empty arrays or empty Objects ({ type: Object, default: () => ({}) }). Then i'm using ternary operator to check the .length or if the property has a value.

Is this the best practice or is there some other way that i'm unaware of?

I would love to get some help with this, so that i do the basics correctly right from the start.

Thanks!

Share Improve this question asked Mar 26, 2019 at 14:07 Robert DanielssonRobert Danielsson 3451 gold badge2 silver badges11 bronze badges 3
  • It's generally better if you include your code in the question instead of just describing it in text. What you are outlining sounds like one of many good solutions, but it's primarily opinion based. – Tholle Commented Mar 26, 2019 at 14:12
  • 1 What you're doing is fine. I usually either have the state empty and return early like if (!state.something) return <p>Loading...</p> or set state to default values that match the correct type, e.g. empty strings or empty arrays, so the component can still render fine but then stuff can pop in after an update. – Tom Finney Commented Mar 26, 2019 at 14:13
  • 1 A common pattern is to initialize state in your class constructor with a loaded property which you would set to true when your API request returns in componentDidMount. If it's false, you can show a loading spinner or something. If it's true, you render the data from your request. Hope that helps. – Justin Smith Commented Mar 26, 2019 at 14:13
Add a comment  | 

3 Answers 3

Reset to default 10

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 />
}

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: []
}
发布评论

评论列表(0)

  1. 暂无评论