Reputation: 1781
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
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
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