3

I'm trying to pass data from my JSON file to my ReactJS app, but receiving the following error:

TypeError: Cannot read property 'mainPage' of undefined

If I try to console.log siteData only, it will work well. I am guessing the problem is probably to do with accessing the object parameters

Can you tell me what I'm doing wrong?

Here is my JSON object:

{
    "data": {
        "mainPage": {
            "navBar": ["HR", "HR1", "HR2"],
            "name": "Name one",
            "agency": "agency one"
        },

        "secondPage": {
            "rank": 2,
            "name": "Name Two",
            "agency": "agency two"
        },

        "thirdPage": {
            "rank": 3,
            "name": "Name Three",
            "agency": "agency three"
        }
    }
}

My .jsx file:

import React from 'react';
import axios from 'axios';
import logo from '../img/company_logo.png';
import '../css/header.scss';

export default class Header extends React.Component {
  constructor() {
    super();
    this.state = {
      siteData: {},
    };
  }

  componentDidMount() {
    axios.get('./data.json')
      .then((res) => {
        this.setState({
          siteData: res.data,
        });
      })
      .catch((err) => {
        console.log(err);
      });
  }

  render() {
    // console.log(this.state);
    const { siteData } = this.state;
    console.log(siteData.data.mainPage);

    return (
      <div className="headerWrapper">
        <a href=".../public/index.html">
        <img src={logo} alt="company_logo" id="companyLogo" />
        </a>
        <ul>
          <li>Navbar_1</li>
          <li>Navbar_2</li>
          <li>Navbar_3</li>
        </ul>
      </div>
    );
  }
}
4
  • siteData.data is undefined. You are probably missing a level of nesting. Try with siteData.mainPage? Commented May 15, 2019 at 10:21
  • looks like siteData.data is undefined, please check if you are getting data using console or alert Commented May 15, 2019 at 10:21
  • if I console.log siteData.data it will show me object Commented May 15, 2019 at 10:21
  • 1
    must be siteData.mainPage Commented May 15, 2019 at 10:24

3 Answers 3

1

The render() method of your Header component is attempting to access the mainPage field of data, which is not initially defined.

Something to keep in mind is that the components render() method will be called before the axios.get() request has completed. This typically means you'll want to render a "loading" message, or skip rendering all together (as shown below), while the axio request is underway.

To apply these ideas, consider revising your component as shown below:

export default class Header extends React.Component {
  constructor() {
    super();
    this.state = {
      /* siteData: {}, Remove this */
    };
  }

  componentDidMount() {
    axios.get('./data.json')
      .then((res) => {
        this.setState({
          siteData: res.data,
        });
      })
      .catch((err) => {
        console.log(err);
      });
  }

  render() {
    // console.log(this.state);
    const { siteData } = this.state;

    /* If siteData not present, then data.json has not been loaded yet, so render nothing */
    if(!siteData) {
        return null;
    }

    /* The siteData is present in the component's state, so we can now access it, and render
    as per usual */
    console.log(siteData.data.mainPage);

    return (
      <div className="headerWrapper">
        <a href=".../public/index.html">
        <img src={logo} alt="company_logo" id="companyLogo" /></a>
        <ul>
          <li>Navbar_1</li>
          <li>Navbar_2</li>
          <li>Navbar_3</li>
        </ul>
      </div>
    );
  }
}
Sign up to request clarification or add additional context in comments.

Comments

1

This happens because componentDidMount runs after the initial render of the component. You can check the lifecycle of a component here where it states:

The componentDidMount() method runs after the component output has been rendered to the DOM.

In the render method you should check if this is null, as with an asynchronous (AJAX) web call, there will be no guarantee you can retrieve the data before the initial render happens, even if you call the AJAX before the render happens.

Comments

0

Can you please try like this? I have listed data statically and stored in state through setState rather than api call. In render, check if there's data in siteData then only map the data and you can also check by length of siteData.

import React from 'react';
import axios from 'axios';
import logo from '../img/company_logo.png';
import '../css/header.scss';

export default class Roles extends React.Component {
constructor() {
    super();
    this.state = {
        siteData: {},
    };
}

componentDidMount = () => {
    let allData = {
        "data": {
            "mainPage": {
                "navBar": ["HR", "HR1", "HR2"],
                "name": "Name one",
                "agency": "agency one"
            },

            "secondPage": {
                "rank": 2,
                "name": "Name Two",
                "agency": "agency two"
            },

            "thirdPage": {
                "rank": 3,
                "name": "Name Three",
                "agency": "agency three"
            }
        }
    }
    this.setState({ siteData: allData.data })
}

render() {
    // console.log(this.state);
    const { siteData } = this.state;
    console.log(siteData && siteData.mainPage);

    return (
        <div className="headerWrapper">
            <a href=".../public/index.html"><img src={logo} alt="company_logo" id="companyLogo" /></a>
            <ul>
                <li>Navbar_1</li>
                <li>Navbar_2</li>
                <li>Navbar_3</li>
            </ul>
        </div>
    );
}
}

Comments

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.