BleachedAxe
BleachedAxe

Reputation: 403

React component doesn't re-render after state change

I am receiving props from a parent container and I am using a Carousel in the child component to show images. My code looks like this :

import React, { Component } from "react";
import ReactDOM from "react-dom";
import style from './ReactSlick.less';
import Carousel from 'nuka-carousel';

export default class ReactSlick extends Component {

  constructor(props) {
    super(props);
    this.state = {
      slides:this.props.pictureArray,
    };
  }}

renderImages() {
 return this.state.slides.map(slide => {
    return (
      <div  key={slide._id}><img src = {slide.image} /></div>
    );
  }, this);
}

return (
  <div className = {style.container}>
      <Carousel autoplay={true}>
        {this.renderImages()}
      </Carousel>
  </div>
);
}}

I have basically tried everything that I could think of including initializing stuff in componentDidMount. Using conditional rendering to check for length of the array and only display if anything exists. The picture Array is an array that contains the images with ids that I want to display in the carousel. I am getting it from the parent container like this:

getImage= (key)=> {
            let {getImage} = this.props;
            let codeArray = [];
        for (let i = 0; i < getImage._id.length; i++) {
             api(getImage._id[i])
            .then(res => codeArray.push(res)),
            (error => console.log(error));}

        return (
            <Bubble
                key={key}
                side="left"
                onImageClick={this.handleLogoClick}>
                <ReactSlick pictureArray={codeArray} />
                </Bubble>
        );
    }

The data is already there when it goes to the child component and I have double checked it too by logging it out in the constructor. I tried putting the API in componentDidMount and setting the state in the child component too but that didn't work either.

I have tested out the same data that I am fetching from the API locally and that seems to work from a JSON but when I get it from a remote server it doesn't work. The only time it shows data from the server is when i make changes to the code and it active reloads then somehow the data is visible there but not the first time.

I think the component doesn't re-render or something. Is there a way to do a force refresh on the child component to make it work? I have read answers that say when setState is called it automatically triggers a re-render but in my case nothing seems to work.

Upvotes: 4

Views: 12891

Answers (2)

Tyro Hunter
Tyro Hunter

Reputation: 755

The problem is the way you iterate against an asynchronous code. Since you are trying to populate codeArray inside a loop from an async api call, you can't use for loop otherwise getImage will not wait for your api call to resolve and therefore returns immediately while codeArray is still empty. So use other types of iterator such as .map that can work with async code.

This might help you, check this out: Using async/await with a forEach loop

/* So this doesn't work */
for (let i = 0; i < getImage._id.length; i++) {
  api(getImage._id[i]).then(res => codeArray.push(res)), (error => console.log(error));
}



/* Need to find a way to wait for all `api(getImage._id[i]).then` calls
   to finish first, only then should `getImage()` return.
   And also take not I am placing `codeArray` in the parent's state */


async getImage () {
  let codeArray = []; // just set the scope using let

  await Promise.all(this.props.images.map(async (image) => {
    codeArray = await api(getImage._id[i]);
  }));

  this.setState({ codeArray })
}

I suggest to call getImage in component did mount:

async componentDidMount() {
  await this.getImage();
}

finally in your render method: <ReactSlick pictureArray={this.state.codeArray} />

/* also set your component statecodeArrayas an empty array to be safe */

P.S: I assumed images was already set from the parent's props (but it could be in state as well). If images will have 10 items, and api() call takes 1 second to finish, therefore getImage() will take around 10 seconds then call setState or update your component (as well as the child ReactSlick)

I hope this helps

Upvotes: 2

Paolo Cruz
Paolo Cruz

Reputation: 1

You have existing props, why would you use the state to iterate loop? Another solution for this one is using self-route. So whenever the API has fetched data successfully.

Upvotes: 1

Related Questions