Search code examples
javascriptreactjsmap-function

Reactjs map function mapping more elements than the ones present in array


I have an array of objects that is like this:

data = [{
            "projectId": 1,
            "projectName": "Progetto 1",
            "clientId": 1,
            "projectDescription": "description1",
            "projectMailingList": "mailingList1",
            "projectCreationDate": "apr 29, 2019",
            "projectVersion": 1
        },
        {
            "projectId": 2,
            "projectName": "Progetto 2",
            "clientId": 1,
            "projectDescription": "description2",
            "projectMailingList": "mailingList2",
            "projectCreationDate": "apr 29, 2019",
            "projectVersion": 1
        },
        {
            "projectId": 3,
            "projectName": "Progetto 3",
            "clientId": 1,
            "projectDescription": "description3",
            "projectMailingList": "mailingList3",
            "projectCreationDate": "apr 29, 2019",
            "projectVersion": 1
        }];

I use the map function for this array, but it's mapping more elements than the ones present. What I have to do is to render a custom component for each of the element of the array, so that would be 3 times, instead it's creating it 7 times. I've tried doing this inside constructor, to check:

this.data.map(projectId => console.log("Call "));

and it correctly prints "Call" for 3 times. Then, in my render method I do this:

return (
      <Slider class="mySlider" ref = {c => (this.slider = c)} {...this.settings}>
        {
          this.data.map(projectId => 
              <ProjectComponent key={projectId} project={projectId} time={this.state.timestamp} originalIndex={ i++ } currentIndex = {this.state.activeSlide}></ProjectComponent>)
        }
      </Slider>
    );

and it creates 7 ProjectComponent s. Why is that? And why 7?

EDIT
This is ProjectComponent:

export class ProjectComponent extends React.Component {


  constructor(props) {
    super(props);
    this.logger = new LoggerService();
    this.state = {
      project: this.props.project //projects contains the needed data
    };
  }

  componentDidMount() {
    // this.logger.info("------ componentDidMount");
    if(this.props.originalIndex === this.props.currentIndex) {
      this.getProjectData();
    }
  }

  componentDidUpdate(prevProps, prevState) {
    if(prevProps.time !== this.props.time
      && this.props.originalIndex === this.props.currentIndex) {
      this.getProjectData();
    }
  }

  render() {
    var homeService = new HomeService();
    if (this.state.isLoading) {
      return (
        <div className="projectContainer">
          <div className="container-fluid">
            <div className="row">
              <div className="col">
                <div className='sweet-loading'>
                  <ClipLoader
                    sizeUnit={"px"}
                    size={100}
                    color={'#FFECA5'}
                    loading={this.state.isLoading} />
                </div> 
              </div>
            </div>
          </div>
        </div>
      );
    } else {
      return (
        <div className="projectContainer">
            <div className="container-fluid">
             <div className="row">
                <div className="col">
                  <ProjectHeaderComponent header={this.state.project}></ProjectHeaderComponent>
               {<ServiceStatusListComponent functionalities={ homeService.getProjectStatusById(this.props.id)} time={this.props.time} 
                  originalIndex = {this.props.originalIndex} currentIndex = {this.props.currentIndex}></ServiceStatusListComponent>}
                </div>
              </div>
            </div>
        </div>
      );
    }
  }

  getProjectData() {
    this.setState({
      project: {},
      isLoading: false
    });
    var homeService = new HomeService();


    this.setState({
      isLoading:false,
      projectResponse: homeService.getProjectStatusById(this.props.id)
    });
  }

}

Solution

  • Moving

    this.data.map(projectId => console.log("Call "));
    

    to render helps when this.data can change in lifecycles. If hardcoded - no difference. If not render is a better place to check state/props right before returning view.

    It's better to console.log something more meaningfull, too.

    In this case it would help debugging because you're iterating over objects, not reaching real projectId property. The same error occurs within render. Instead of idetifier you're using refs to object - keying fails allowing for <ProjectComponent/> duplicates.

    There should be

    this.data.map( project =>  
              <ProjectComponent key={project.projectId} project={project}
    

    The <ProjectComponent /> source was not needed - component can't duplicate itself. That's why its parents needs inspection, f.e. <Slider /> (direct parent) and the 'main' component. If <Slider /> was rendered once then only the second can be a source of error.

    As we have <ProjectComponent /> source, we see errors there:

    • using this.props.id while no id prop passed;
    • unnecessary creating homeService when condition can lead to render 'loading'