0

I've been facing a weird issue lately with my React App. I'm trying to parse a JSON object that contains arrays with data. The data is something like this:

{"Place":"San Francisco","Country":"USA", "Author":{"Name":"xyz", "Title":"View from the stars"}, "Year":"2018", "Places":[{"Price":"Free", "Address":"sfo"},{"Price":"$10","Address":"museum"}] }

The data contains multiple arrays like the Author example I've just shown. I have a function that fetches this data from a URL. I'm calling that function in componentDidMount. The function takes the data i.e responseJson and then stores it in an empty array that I've set called result using setState. In my state I have result as result:[]. My code for this would look something like this:

this.setState({result:responseJson})

Now, when I've been trying to access say Author Name from result I get an error. So something like this:

{this.state.result.Author.Name}

I'm doing this in a function that I'm using to display stuff. I'm calling this function in my return of my render function. I get an error stating :

TypeError:Cannot read property 'Name' of undefined. I get the same error if I try for anything that goes a level below inside. If I display {this.state.result.Place} or {this.state.result.Country} it's all good. But if I try,say {this.state.result.Author.Title} or {this.state.result.Places[0].Price} it gives me the same error. Surprising thing is I've parsed this same object in a different component of mine and got no errors there. Could anyone please explain me why this is happening?

If I store the individual element while I setState in my fetch call function, I can display it. For example:

{result:responseJson,
 AuthorName:responseJson.Author.Name
 }

Then I'm able to go ahead and use it as {this.state.AuthorName}.

Please help me find a solution to this problem. Thanks in advance!

2 Answers 2

1

It could be that your state object is empty on the first render, and only updated with the data from the API after the request has completed (i.e. after the first render). The Name and Place properties don't throw an error, as they probably resolve to undefined.

Try putting an if block in your render method to check if the results have been loaded, and display a loading indicator if they haven't.

I'm guessing your initial state is something like this:

{ results: {} }

It's difficult to say without seeing more code.

[EDIT]: adding notes from chat

Data isn't available on first render. The sequence of events rendering this component looks something like this:

  1. Instantiate component, the initial state is set to { results: [] }
  2. Component is mounted, API call is triggered (note, this asynchronous, and doesn't return data yet)
  3. Render method is called for the 1st time. This happens BEFORE the data is returned from the API request, so the state object is still {results: [] }. Any attempts to get authors at this point will throw an error as results.Authors is undefined
  4. API request returns data, setState call updates state to { results: { name: 'test', author: [...] } }. This will trigger a re-render of the component
  5. Render method is called for the 2nd time. Only at this point do you have data in the state object.
Sign up to request clarification or add additional context in comments.

7 Comments

The Name and Place properties show the correct output. I've already done that, trying an if block and displaying only when a condition satisfies. It works then. My initial state is: {result:[]}
Try putting a console log of the state in the render method. That should tell you if the state has sufficient data to render the authors
It gives me the same error if I try to console log it.
what is the result of the console logs? it would be really helpful if you could add some more code
TypeError: Cannot read property 'Name' of undefined. render(){ > 75 | console.log(this.state.result.Author.Name) 76 | return( 77 | <div> 78 | {this.displayResult()} </div>
|
1

If this state evolves, means it is changed at componentDidMount, or after a fetch or whatever, chances are that your state is first empty, then it fills with your data.

So the reason you are getting this error, is simply that react tries to get this.state.result.Author.Name before this.state.result.Author even exists.

To get it, first test this.state.result.Author, and if indeed there's something there, then get Author.Name like this.

render(){
    return(
        <div>
            {this.state.result.Author ? this.state.result.Author.Name : 'not ready yet'}
        </div>
    );

}

[EDIT] I'll answer the comment here: It's just because they are at a higher level in the object.

this.state.result will always return something, even false if there is no result key in your state (no result key in your constructor for instance when the component mounts).

this.state.result.Country will show the same error if result is not a key of your state in your constructor. However, if result is defined in your constructor, then it will be false at first, then become the data when the new state populates.

this.state.result.Author.Name is again one level deeper...

So to avoid it, you would have to define your whole "schema" in the constructor (bad practice in my opinion). This below would throw no error when getting this.state.result.Author.Name if I'm not mistaken. It would first return false, then the value when available.

constructor(props){
super(props);
    this.state={
        result: {
            Author: {}
        }
    }
}

3 Comments

Got it. But I'd like to know how and why does something like {this.state.result.Country} or {this.state.result.Place} work out?
I replied in the answer above ;) Please mark as accepted answer if you consider that it is relevant. Cheers
Appreciate your help.Thank you. Someone else answered it a little earlier than you. Can only have one accepted answer, sorry. Thank you for your answer and your explanation behind it.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.