Passing JSON data down to components, react

Hello everyone, I am having an issue with passing data from a json file down to one of my components.
First I get the data from the JSON file in my app.js

getResumeData(){
    $.ajax({
      url:'/addnewitems.json',
      dataType:'json',
      cache: false,
      success: function(data){
        this.setState({newitemdata: data}); console.log(data)
      }.bind(this),
      error: function(xhr, status, err){
        console.log(err);
        alert(err);
      }
    });
  }

  componentDidMount(){
    this.getResumeData();
  }

As far as I can tell this works because the console.log shows me an object with my data in it.
Then I pass it to the component.

<Route path='/Services'
  render={(props) => <Services {...props} />}
/>

Finally I use the data to create some HTML

class Services extends React.Component {
  constructor(props) {
    super(props);

    this.state = {
     ServiceItems: this.props.data
    };
  }

  render() {
    return (
      <div className='row'>
        {this.state.ServiceItems.map(({ id,  imageUrl, title, description, ...otherSectionProps}) => (
          <div className="servicesgrid container"> 
    <div className='serviceitem'> 
      <img src={imageUrl}></img>
      <h1 className='serviceitemtitle'>{title.toUpperCase()}</h1>
      <p className="serviceitemdesc">{description}</p>
      <button className="btn btn-info">Shop now</button>
    </div>
  </div>
        ))}
      </div>
    );
  }
}

It’s trowing an error message from this last step. I’ve doubled checked everything I know of and it’s still not getting where I want it. “TypeError: Cannot read property ‘map’ of undefined”
I know I am dumping a lot of code here, but I am honestly stuck. I’ve been at it a few hours and I’m no closer to solving it.
I thank you in advance, I hope it will only take a few minutes of your time to help me.

You are rendering your components before the data has been fetched, so your component is complaining that it cannot map over something that is still undefined.

A common workaround is to display some UI element while the data is being fetched like a spinner or loader.

In react is pretty trivial:

// in your parent keep track of a loading state
state = {
 loading: true,
 data: [] // don't know yet
}

// in your child
if(props.loading) { // render spinner }
else { // render list component }

This is just a simple pseudo-code but hope it will help :slight_smile:

2 Likes

This is the right solution, i can tell. However I am a little lost on how I should implement it.

render() {
    if (this.props.loading) return <h1>Still loading</h1>
    else 
    return (
      <div className='row'>
        {this.state.ServiceItems.map(({ id,  imageUrl, title, description, ...otherSectionProps}) => (
          <div className="servicesgrid container"> 
    <div className='serviceitem'> 
      <img src={imageUrl}></img>
      <h1 className='serviceitemtitle'>{title.toUpperCase()}</h1>
      <p className="serviceitemdesc">{description}</p>
      <button className="btn btn-info">Shop now</button>
    </div>
  </div>

This is what I have so far, but I think I am missing something from your solution.

Well, is loading being passed as a prop to the component?

if (this.props.loading)

Anyway a very simple implementation

class App extends React.Component {
  state = {
    loading: false,
    data: []
  }

  componentDidMount() {
    this.setState({loading: true})

    // async action.
    // on success / error toggle loading and data
  }

  render() {
    const { loading, data } = this.state;

    if (loading ) {
      return (<p>loading...</p>)
    }
    return (
      <MyList data={data} />
    );
  }
}

Hope this helps :+1:

1 Like

Thank you. I have been working on this project after 8 hour shifts, so my brain has been a little fried.
Now that I have a day off I can see my errors. Thanks for your help

So I talked myself up but It appears that on a day off I am just as incompetent.


    this.state = {
      loading: false,
     serviceitems: {}
    };
  }

  componentDidMount() {
    this.setState({loading: true})
  }
  render() {
    const { loading, data } = this.state;
    if (loading ) {
      return (<p>loading...</p>)
    }
    if(this.props.data){
      var serviceitems = this.props.data.serviceitems.map(function(serviceitems){
        return <div key={serviceitems.title} className="columns portfolio-item">
           <div className="item-wrap">
               <div className="overlay">
                  <div className="portfolio-item-meta">
                 <h5>{serviceitems.title}</h5>
                     <p>{serviceitems.category}</p>
                  </div>
                </div>
          </div>
        </div>
      })
    }
    else 
    {console.log('service item failed')}
    ````
The page just displays the loading forever, and every edit I make seems to just break the whole page
<Route path='/Services'

  render={(props) => <Services {...props} serviceitems={this.state.data} isAuthed={true} />}

/>

Here’s the passing from my app.js
I feel bad for not being able to solve this myself with all the help you’ve given me. I really appreciate your expertise.

I think the steps should be like this:

  1. set loading to true.
  2. when the component rendered, call componentDidMount (cDM).
  3. inside cDM you can call an async function when it is done, set the loading to false.
  4. render the data you got from cDM.

your code will always show the loading text since after it was rendered, React will call cDM and you set the loading state to true, so it will always show loading text.

1 Like

Alright, I understand that.
So I have changed the code a bit now, I added this.

componentDidMount() {
    async function setthing() {
    this.setState({loading: false})
    }
    setthing();
  }

Now I’ve made some progress, it loads for a second and then crashes.
“TypeError: Cannot read property ‘setState’ of undefined”

I’m linking the github repo in case anyone else wants to look at the code and help.
Again, I am super grateful for everyone’s assistance here.

After working (pretty much all day :frowning:) I think I have found the main problem .
After console.log-ing everything, I found that my main retrieval component is returning undefined.

getResumeData(){
    $.ajax({
      url:'/addnewitems.json',
      dataType:'json',
      cache: false,
      success: function(data){
        this.setState({propdata: data}); console.log(this.propdata)
      }.bind(this),
      error: function(){
        alert('THE AJAX IS BROKEN');
      }
    });
  }

I copied this from someone else’s project, so if anyone knows a way to fix it/use something better, I am all ears.

getResumeData does not explicitly return a value, so it returns undefined by default.

1 Like

Thanks for your help again Randell, so how do I get it to return my JSON data instead?
Or should I use another method?

Not sure why you are using jQuery with React. You can use fetch instead and then use setState when the data is retrieved.

1 Like

Good advice as always.
I seem to be running in to a bit of a wall with the fetch request though.

getResumeData() {
    fetch('./addnewitems.json')
    .then(function (data) {
      return data.json();
    })
    .then(function (data) {
    this.setState({propdata: data});
    })
    .catch(function (err) {
      console.log('err');
    });
  }

I am betting that there’s a beginner’s mistake here, I just can’t see it yet.
I feel like I am on the home stretch, after this I can finally stop asking for help!

Would you mind posting the full code, so we can see the full structure?

What seems to be the problem currently?

1 Like

Sorry about these delays. I’ve been getting called in to work a lot because of this whole pandemic thing.
This is the full component. The main problem is the fetch is not getting the data from the JSON file.
From what I can tell it is passing correctly to the component (what I thought was the problem initially).

import React, { Component } from 'react'
import './App.css';
import Home from './components/home'
import Contact from './components/contact'
import Blog from './components/blog'
import Services from './components/services'
import Navbar from  './components/navbar'
import Header from './components/header'

import {  Route, BrowserRouter } from 'react-router-dom';


class App extends Component {
  constructor(props){
    super(props);
    this.state = {
     propdata: {}
    };

  }


  getResumeData() {
    fetch('./addnewitems.json')
    .then(function (data) {
      return data.json();
    })
    .then(function (data) {
    this.setState({propdata: data});
    })
    .catch(function (err) {
      console.log('err');
    });
  }

  /*
   */ 
  
  

  componentDidMount(){
    this.getResumeData();
  }

render () {
  return (
   <BrowserRouter>
    <div className="App">
  <div className="fixedview"> 
    <Header /> 
    <Navbar />
  </div>
    <div className="viewthatchanges" >
    <Route exact path="/" component={Home} />
    <Route path='/Services'
  render={(props) => <Services {...props} propdata={this.state.propdata} />}
/>
<Route path="/blog" component={Blog} />
<Route path="/contact" component={Contact} />
    </div>
  </div>
  </BrowserRouter>
  );
}
}
export default App;

You can see the whole code here :

Can you provide a reproducible demo?
Looks like the Github project is behind with what you showed in the previous posts (it still uses Ajax).

Anyway my guess is here:

I think that this is now bounded by the callback function instead of your App class.

1 Like

I forgot to update the github, sorry!
Here it is on codesandbox.
https://codesandbox.io/s/github/Imstupidpleasehelp/FitnessWebsite-

As @Marmiz mentions above, the this in this.setState is not the component. You could use arrow functions instead and avoid this issue as arrow functions refer to their surrounding scope which is what you want.

1 Like
componentDidMount(){
    fetch('/addnewitems.json')
       // .then(res => res.text())          
       // .then(text => console.log(text))
    .then(res =>
      this.setState({
        propdata: res,
      })
    )
    .then(propdata => console.log(propdata))
    }

The error messages are gone, and I managed to get my JSON file to show up in my console with that res.text() line. However now It is still returning undefined when I don’t use the text or when I try to set propdata to the value of text Or Res.

Please and thank you.

componentDidMount(){
    fetch('/addnewitems.json')
       .then(res => res.json())          
           .then(res =>{
      this.setState({
      ....this, propdata: res,
      })
    }
)
    
}
1 Like