Sachihiro
Sachihiro

Reputation: 1781

Re render when data from async function is ready

I have an async function that GET the notes from network, the problem that I have is that it tries to render an empty array of data, therefore I get this error saying that item.id is undefined because the array is empty. I tried to put a condition if (data.length === 0) return <Text>No Entries</Text> but then it does not re render anything, even though when I console.log(data) I can see the data has arrived. Is there any way to re render when data has arrived, or any other way around this?

export default class NoteList extends Component {
  render() {
    const { data} = this.props;
    return (
      <View style={styles.cardView}>
        <FlatList
          numColons={data.length}
          data={data}
          renderItem={({ item: { name, content, id } }) => {
            return (
              <View>
                <NoteCard 
                  name={name}
                  content={content}
                />
              </View>
            );
          }}
          keyExtractor={(item) => item.id}
        />
      </View>
    );
  }
}

How to prevent this:

TypeError: TypeError: undefined is not an object (evaluating 'item.id')

I also get this error, but I think it is related to the management of the first problem.

Warning: Can't perform a React state update on an unmounted component. This is a no-op, but it indicates a memory 
leak in your application. To fix, cancel all subscriptions and asynchronous tasks in %s.%s, the componentWillUnmount method,

Upvotes: 1

Views: 1413

Answers (2)

Jurrian
Jurrian

Reputation: 849

The problem you have, lies in the parent where data is saved in the state. Therefor you get the warning about updating the state of an unmounted component. There are a lot of different ways to fix this. One way I like (because of the readability), is using a variable for when the component mounts. A simple example:

class News extends Component {
  _isMounted = false;
  constructor(props) {
    super(props);
    this.state = {
      news: [],
    };
  }
  componentDidMount() {
    this._isMounted = true;
    axios
      .get('https://hn.algolia.com/api/v1/search?query=react')
      .then(result => {
        if (this._isMounted) {
          this.setState({
            news: result.data.hits,
          });
        }
      });
  }
  componentWillUnmount() {
    this._isMounted = false;
  }
  render() {
    ...
  }
}

Now when the data is set, the NoteList component will automatically get updated. However what happens when api call fails. To prevent stale data, I like to use conditional rendering. Just like you suggested:

export default class NoteList extends Component {
  render() {
    const { data } = this.props;

    if (data) {
      return (
        <View style={styles.cardView}>
          <FlatList
            numColons={data.length}
            data={data}
            renderItem={({ item: { name, content, id } }) => {
              return (
                <View>
                  <NoteCard name={name} content={content} />
                </View>
              );
            }}
            keyExtractor={item => item.id}
          />
        </View>
      );
    }
  }
}

Upvotes: 1

lukbuz
lukbuz

Reputation: 98

A common way to do this in React is to keep track of when data is being fetched. This can be done e.g. by having a isFetching field in your state:

// This would be your default state
this.state = {
  isFetching: false
};

Then, when you fire off the request (preferably in componentDidMount) you set isFetching to true using:

this.setState({ isFetching: true });

And finally, when the data arrives, you set it to false again:

this.setState({ isFetching: false });

Now, in your render function you can do something like this:

render () {
 return (
    <div className="something">
      <h3>Some content</h3>
      {this.state.isFetching ? <LoadingComponent /> : (
         <ul>
           {listItems}
         </ul>
      )}
    </div> 
  )
}

By using state, you don't have to worry about telling your component to do something, instead it reacts to changes in the state and renders it accordingly.

Upvotes: 0

Related Questions