4

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.

halfer
  • 19,824
  • 17
  • 99
  • 186
BleachedAxe
  • 393
  • 2
  • 5
  • 20
  • Change this `this.state.slides.map` to `this.props.pictureArray.map`. You don't need state for this one – invisal Apr 06 '19 at 01:43
  • Tried that too, doesn't work. That's why i assigned it to state that maybe it re-renders but had no luck. – BleachedAxe Apr 06 '19 at 02:09
  • One things is that seems to be working is when i put a button on the page and that button when clicked runs forceUpdate(). Is there any way of running this method automatically like 1 second after rendering? – BleachedAxe Apr 06 '19 at 02:26
  • Can you show us how the parent's `pictureArray` is going to be set again? Also using `native for loop` is almost always inferior vs `.map or .forEach, etc` – Tyro Hunter Apr 06 '19 at 02:34
  • It's in the code that I have put up. I am taking the picture array and setting it to slides in the state of child component. I am using a for loop to push data to code array in the parent container in a promise so definitely the data is ready by the time it's pushed to the child container. – BleachedAxe Apr 06 '19 at 02:43

2 Answers2

2

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

Tyro Hunter
  • 755
  • 1
  • 8
  • 20
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.