Mike26
Mike26

Reputation: 129

Cannot read property of undefined in Reactjs

I tried to read some data with some ajax request, but I am not sure if my code is correct. Here is the code:

interface IProps {
  data: IScheduler;
}

interface IState {
  list: IScheduler;
}

export default class Page extends React.Component<IProps, IState> {
  constructor(props: IProps) {
    super(props);
    this.state = {
        list: this.props.data,
    };
}

public componentWillMount() {
    this.getData();
}

public getData = () => {
  axios.get(myURL)
        .then( (response) => {
            this.setState({list: response.data});
            console.log(response.data.generatedDate); //works
        })
        .catch( (error) => {
            console.log("DIDN'T CATCH DATA", error);
        });
  }

public render(): JSX.Element {
    const data: IScheduler = this.state.list;

    // getting an error
    console.log(data.creationDate);

    return (
        <div className="container">
            <MyListCmp data={data}/>
        </div>
    );
}

I see the error:

Uncaught TypeError: Cannot read property 'list' of undefined

When I try:

console.log(data); // or this.state.list

it's just undefined.

Data example:

{
   "generatedDate": "12.12.2017",
   "meetings": [

          ...
    ]
}

The same error occurs in the MyListCmp when I want to read 'meetings' or 'generatedDate':

Uncaught TypeError: Cannot read property 'generatedDate' of undefined
Uncaught TypeError: Cannot read property 'meetings' of undefined

How can I read it? Is this possible that in render() or return() data hasn't fetched yet?

I can tell that if I do like this:

 const data: IScheduler =  {
   generatedDate: "12.12.2017",
   meetings: [

          ...
    ]
}

It works, but this is not the point. I need to fetch it from some URL. I will appreciate every advice.

Upvotes: 3

Views: 6098

Answers (2)

Rich Churcher
Rich Churcher

Reputation: 7664

This is an example of trying to depend on a value without checking it or providing a default. You wrote:

public render(): JSX.Element {
  const data: IScheduler = this.state.list;

  // getting an error
  console.log(data.creationDate);

render() gets called early, probably before the response has time to come back. What you want to do is provide a way for it to render correctly even if the data isn't available yet. Often that means a loading animation, but you can also just conditionally render the data:

{this.state.list && <MyMeetingList meetings={this.state.list} />}

Why does this work?

The above is a common shortcut for "only do the second thing if the first thing evaluates truthy". If this.state.list is undefined, that's falsy so the expression is short-circuit evaluated to false, and the JSX on the right hand side of the && is ignored.

Why is this useful?

React render functions get called a lot. If you want to try an experiment sometime, put a breakpoint in render or stick a log in there. Depending on the complexity of the component and how often its data changes, you may be surprised how often it fires. This means it's going to be called at least once before any async operations have time to complete, and you need to think ahead about sensible defaults for any values it requires or (as above) test to see if they exist yet.

Upvotes: 2

basarat
basarat

Reputation: 276393

How can I read it? Is this possible that in render() or return() data hasn't fetched yet?

Yes. Hence the undefined 🌹

Upvotes: 0

Related Questions