Paulos3000 Paulos3000 - 1 month ago 19
React JSX Question

Attempt to render state value from ajax call in componentDidMount failing (React)

I've made an AJAX call in React with Axios, and I'm a bit confused about how the response is dealt with.

Here is my code:

componentDidMount() {
axios.get('https://jsonplaceholder.typicode.com/users')
.then( res => {
const users = res.data
this.setState({ users });
});
}

render() {
console.log(this.state.users)
return (
<div>
{this.state.users.map( user => <p>{user.name}</p>)}
</div>
)
}


When I console.log the results, two values are returned:


  1. []
    - the empty array assigned to the initial state

  2. The expected array from the API.



This presumably can be explained by the state value being returned once on initialisation, and then again with componentDidMount.

My problem arises in how I access the
this.state.users
value. Obviously any time I access this, I want the values returned from the AJAX response, but if, for example I try to render the
name
property from the first object in the array...

{this.state.users[0].name}


...it attempts to access
[]
, and thus returns nothing.

However, if I try to iterate through the arrays elements...

{users.map( user => <p>user.name</p> )}


...it returns the values as expected.

I don't know why it works with the second example but not the first. Surely if the first is attempting to pull data from the initial state value (an empty array), then when mapping through
this.state.users
it would also be attempting to map through an empty array and return nothing, or an error.

I'm obviously not fully understanding the React rendering process here and how componentDidMount works in the component lifecycle. Am I wrong in trying to assign the response value directly to state?

Would appreciate if anyone could help to clear this up.

Answer

When you use map() on this.state.users initially, it will do so over an empty array (which won't render anything). Once the response arrives, and this.setState({ users }) is called, render() will run again. This time, the array of users is no longer empty and the map() operation will be performed on each of the users in the array.

However, when you use {this.state.users[0].name} initially on the empty array, you're trying to access the name property of an object that doesn't yet exist — which will result in an error. To prevent the error, you could do a check before accessing the properties of the object:

{this.state.users.length > 0 && this.state.users[0].name}

Now it will behave the same way as in the first example. I.e. the first render() won't actually do anything, since this.state.users.length = 0. But as soon as this.setState() is called with new users, render() will run again, and this time this.state.users[0].name is available.