0
votes

I'm React newbie and struggling. Following snippet gives the following error

"Uncaught TypeError: Cannot read property 'creationDate' of undefined".

If I move code from populateTableRows and creationDate functions inside render, everything works nicely. SurveyList gets it's data from another component. I know this is pretty ugly component and all other suggestions are welcome too, but I'm mostly interested in this one particular error.

import React from 'react';
import ReactDOM from 'react-dom';
import { Table, Tr, Td } from 'reactable';

class SurveyList extends React.Component{
  constructor(props) {
    super(props);
    this.state = {
      isSaved: false,
      surveys: []
    };
    this.creationDate = this.creationDate.bind(this);
    this.populateTableRows = this.populateTableRows.bind(this);    
  }

  creationDate (obj){
    return new Date(obj._id.time).toISOString().slice(0,10);
  }

  populateTableRows(surveys){
    var surveyRows = [];
    surveys.forEach(function(obj){
      surveyRows.push(
        <Tr key={obj.surveyId}>
          <Td column="SurveyId">{obj.surveyId}</Td>
          <Td column="Survey name">{obj.surveyName}</Td>
          <Td column="Creation date">{this.creationDate(obj)}</Td>
          <Td column=""><ModalDialog key={obj.surveyId}
                                     survey={obj}
          /></Td>
        </Tr>
      );
    });
    return surveyRows;
  }

  render() {
    var surveys = Array.from(this.props.surveys);
    var surveyRows = this.populateTableRows(surveys);
    return (
      <Table className="table" id="table" sortable={true} filterable={['SurveyId', 'Survey name', 'Creation date']}>
        {surveyRows}
      </Table>
    )
  }
}
1
I think this has more do with scope and closures perhaps? Try the following surveys.forEach(() => { ..... An anonymous function will hold the scope you are after for the this.creationDate call. Check this ebook out too: github.com/getify/You-Dont-Know-JS/tree/master/… - ctrlplusb
Thanks, I definitely have to read that. - JohnP

1 Answers

2
votes

The comment by @ctrlplusb is correct. When you use a function keyword as you have done in your surveys.forEach call, its contents get a new scope - and therefore a new this, which is undefined as it belongs to no object. There are several solutions.

The prettiest is to use the new fat arrow ("lexical this") syntax available in ES2015 through Babel. It creates a function that maintains the scope in which it was defined. E.g.:

surveys.forEach( obj => surveyRows.push(/* ... */) );

However, the simplest is to use the second argument that forEach takes, which is the this to use:

surveys.forEach( function ( obj ) {
  surveyRows.push(/* ... */);
}, this );