Jamie
Jamie

Reputation: 2081

How to get json data in react render?

What I am trying to do grab json data to render it as an element. Here is what I have, but this.images continues to come up empty(and undefined/null if I don't set it at the top.

import React, { Component } from 'react';
import axios from 'axios';

export default class Grid extends Component {
  constructor(props) {
    super(props);
    this.images = [];
  }

  componentWillMount() {
    axios.get('grid-config.json')
    .then((res) => {
      this.setImageArray(res.data);
    });
  }

  setImageArray(imageArray) {
    let newArray = [];
    for(let i = 0; i < imageArray.length; i++) {
      newArray.push(imageArray[i]);
    }
    this.images = newArray;
   }


  render() {
    const postData = this.props.images;
    console.log(this.images);
    return (
      <div>
       hello
      </div>
    );
  }
}

Upvotes: 0

Views: 859

Answers (3)

Mark Williams
Mark Williams

Reputation: 2308

You should use the component's state to hold the image data; when this is updated it will cause the component to render (React will call its render function.)

So, for example, set the component's state something like this:

    setImageArray(imageArray) {
    let newArray = [];
    for(let i = 0; i < imageArray.length; i++) {
      newArray.push(imageArray[i]);
    }
    this.setState({images: newArray });
   } 

and also initialise this, for example, in the component's constructor:

constructor(props) {
    super(props);
    this.state = { images: [] };
  }

You access the data in the render function as this.state.images.

{See the section entitled A Stateful Component at https://facebook.github.io/react/ }

Upvotes: 3

Sachin
Sachin

Reputation: 3540

  1. React components will call render only if the state is changed (except in special cases like initial render or when forceUpdate is called).

  2. Your axios call is async and component is already rendered by the time it executes.

  3. Thus, the render function is called when images is [], and never called again.

  4. To force a re-render, you have to store the response in state, as pointed in multiple answers or use forceUpdate (not recommended).

EDIT

class Application extends React.Component {
  constructor(props) {
    super(props)
    this.myName = "Sachin"
  }

  componentDidMount() {
    setTimeout(() => {
      this.myName = 'Jamie';

      // If this is commented out, new value will not reflect in UI.
      this.forceUpdate()
    })
  }
  render() {
    return (
      <div>
        <h1>Hello, {this.myName}</h1>
      </div>
    );
  }
}

See this code pen link : https://codepen.io/sach11/pen/ayJaXb

Its very similar to your code, just replaced axios call with a setTimeout.

On line 16, I have a forceUpdate(), which forces a re-render and reflects the new value of this.myName. Otherwise although value is updated, it does not reflect as render is not called again.

Upvotes: 0

Artem Mirchenko
Artem Mirchenko

Reputation: 2170

in this case you must only manipulate with your state instead of class property. Use setState method to update your component state. And render images from your updated state.

export default class Grid extends Component {
  constructor(props) {
   super(props);
   this.state = { images: [] };
  }

 componentWillMount() {
  axios.get('grid-config.json')
   .then((res) => {
    this.setImageArray(res.data);
  });
 }

 setImageArray(imageArray) {
  let newArray = [];
  for(let i = 0; i < imageArray.length; i++) {
    newArray.push(imageArray[i]);
  }
  this.setState({ images: newArray });
 }


 render() {
  const postData = this.state.images;
  console.log(postData);
  return (
   <div>
    hello
   </div>
  );
 }
}

Try this code to render images;

Upvotes: 0

Related Questions