34

I have a react component that I wish to populate with images using the Dropbox api. The api part works fine, but the component is rendered before the data comes through & so the array is empty. How can I delay the rendering of the component until it has the data it needs?

var fileList = [];
var images = [];
var imageSource = [];

class Foo extends React.Component {

 render(){
  dbx.filesListFolder({path: ''})
  .then(function(response) {
   fileList=response.entries;
   for(var i=0; i<fileList.length; i++){
    imageSource.push(fileList[0].path_lower);
   }
   console.log(imageSource);
   })

  for(var a=0; a<imageSource.length; a++){
   images.push(<img key={a} className='images'/>);
  }

  return (
   <div className="folioWrapper">
    {images}
   </div>
  );
 }
}

Thanks for your help!

2
  • I would only mount that component once you have the images. So whoever mounts this component (the parent), should fetch the images then mount the component. Commented Jul 4, 2017 at 16:59
  • You shouldn't be doing all of that work inside your render method. Where are you changing the state? The component renders after updating the state. Commented Jul 4, 2017 at 17:00

3 Answers 3

40

Changes:

1. Don't do the api call inside render method, use componentDidMount lifecycle method for that.

componentDidMount:

componentDidMount() is invoked immediately after a component is mounted. Initialization that requires DOM nodes should go here. If you need to load data from a remote endpoint, this is a good place to instantiate the network request. Setting state in this method will trigger a re-rendering.

2. Define the imageSource variable in state array with initial value [], once you get the response update that using setState, it will automatically re-render the component with updated data.

3. Use the state array to generate the ui components in render method.

4. To hold the rendering until you didn't get the data, put the condition inside render method check the length of imageSource array if length is zero then return null.

Write it like this:

class Foo extends React.Component {

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

    componentDidMount(){
        dbx.filesListFolder({path: ''})
          .then((response) => {
              let fileList = response.entries;
              this.setState({
                  imageSource: fileList
              });
          })
    }

    render(){
        if(!this.state.imageSource.length)
            return null;

        let images = this.state.imageSource.map((el, i) => (
            <img key={i} className='images' src={el.path_lower} />
        ))

        return (
            <div className="folioWrapper">
                {images}
            </div>
        );
    }
}
Sign up to request clarification or add additional context in comments.

1 Comment

you need to bind set state to the function inside the promise
6

You should be using your component's state or props so that it will re-render when data is updated. The call to Dropbox should be done outside of the render method or else you'll be hitting the API every time the component re-renders. Here's an example of what you could do.

class Foo extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      imageSource: []
    }
  }

  componentDidMount() {
    dbx.filesListFolder({ path: '' }).then(function(response) {
      const fileList = response.entries;

      this.setState({
        imageSource: fileList.map(file => file.path_lower);
      })
    });
  }

  render() {
    return (
      <div className="folioWrapper">
        {this.state.imageSource.map((image, i) => <img key={i} className="images" src={image} />)}
      </div>
    );
  }
}

If there are no images yet, it'll just render an empty div this way.

2 Comments

I think this code won't work, because the Binding is missing.
@cloud_traveler what do you mean? What binding is missing?
0

First off, you should be using the component's state and not using globally defined variables.

So to avoid showing the component with an empty array of images, you'll need to apply a conditional "loading" class on the component and remove it when the array is no longer empty.

2 Comments

That's the issue I'm struggling to understand - how to enable the component to know when the array is no longer empty.
See Mayank Shukla answer.

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.