1

I'm trying to create a table React component that will take in data and headings as props. I've run into a problem where I can't get the forEach method of Map won't create the necessary children elements.

The input prop for columns looks like this:

let cols = {
  payment: 'Payment',
  date: 'Processing Date',
  amount: 'Amount',
  payee: 'Payee'
};

Here's a method of my React table component that generates the heading elements:

generateTableHead() {
  let columnsMap = new Map((Object.entries(this.props.columns)));
  let i = 0;
  return (
    <tr>
      {columnsMap.forEach((columnValue) =>
        <th key={i++}>
          {columnValue}
        </th>
      )}
    </tr>
  );
}

The problem is that the <th> children don't exist in the <tr> object that's returned; I get an emtpy <tr></tr>.

However, if I use Array.prototype.map, the children <th> elements WILl be created properly.

generateTableHead() {
  let columnsArray = Object.entries(this.props.columns);
  return (
    <tr>
      {columnsArray.map((column, index) =>
        <th key={index}>{column[1]}</th>
      )}
    </tr>
  );
}

I prefer the first version because the code is slightly more understanding since I'm going to reference keys and values in the Map object, rather than using array indices in the second version.

I'm sure there is a very simple explanation for this, or I've made a very simple error that I just can't spot. If anyone can point out the problem, I'd greatly appreciate it!

Clinton Chau
  • 557
  • 2
  • 12

3 Answers3

6

This is because forEach() does not return anything, unlike map() or reduce().

From https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/forEach?v=control

forEach() executes the callback function once for each array element; unlike map() or reduce() it always returns the value undefined and is not chainable. The typical use case is to execute side effects at the end of a chain.

errata
  • 5,695
  • 10
  • 54
  • 99
  • Ah! I must be blind... the fact it returns undefined is right there in the documentation. I prefer this answer because it is concise and to the point. – Clinton Chau May 12 '17 at 17:28
0

Reason is, forEach will not return any thing, that is used just to iterate the array elements, but map used to return something for each entry of array. If you want to use the forEach you can use in this way, just use a variable to store the ui items and iterate the array using forEach and push the items in that variable, after that just return a tr with all the td's, Like this:

generateTableHead() {
    let columnsMap = new Map((Object.entries(this.props.columns)));
    let uiItems = [];
    let i = 0;
    columnsMap.forEach((columnValue) =>
       uiItems.push( <th key={i++}>
                        {columnValue}
                     </th>)
   )
   return (
      <tr> {uiItems} </tr>
   );
}

Check this snippet to understand the difference between map and forEach:

let a = [1,2,3,4,5];

let b = a.map(e => e + 1); //it will return e + 1;

let c = a.forEach(e => e + 1);   //it will not return anything

console.log('b', b);

console.log('c', c);
Mayank Shukla
  • 100,735
  • 18
  • 158
  • 142
0

You don't have to depend on the array indices even in the second version. You can use Object.values instead of Object.entries.

generateTableHead() {
  let columnsValues= Object.values(this.props.columns);
  let i = 0;
  return (
    <tr>
      {columnsValues.map((columnsValue) =>
        <th key={i++}>{columnsValue}</th>
      )}
    </tr>
  );
}
Tharaka Wijebandara
  • 7,955
  • 1
  • 28
  • 49