2

I am trying to enter to values in a textbox and print them on button click by setting react state. I am pushing object in array initialized in a state the value of text boxes is pushing but not getting mapped. it giving error this.state.elements.map is not a function

import React, { Component } from 'react';
import logo from './logo.svg';
import './App.css';

class App extends Component {

  constructor(props) {
    super(props);
     this.state = {
       elements: [
        {element1 :"A",element2: "A"},
        {element1 :"B",element2: "B"},
        {element1 :"C",element2: "C"},
        {element1 :"D",element2: "D"}

       ],
    }
    this.elementInput = "";
    this.elementInput_2 = "";

}
addItems()
  {
    debugger
    this.setState({
     elements : this.state.elements.push({
       element1 : this.elementInput.value ,
       element2 : this.elementInput_2.value
     })

   })
   console.log()
  }
  render() {
    return (
      <div>
       <input type="text" placeholder="Enter Element" ref={(input) => this.elementInput = input} />
       <input type="text" placeholder="Enter Element" ref={(input) => this.elementInput_2 = input} />
       <button onClick={this.addItems.bind(this)}>Add</button>
       {
          this.state.elements.map((item,index)=> {
            return  <p key={index}>{item.element1 + " " + item.element2}</p>
          })
        }
      </div>
    );
  }
}

export default App;
owais latif
  • 61
  • 1
  • 2
  • 14
  • 2
    You can use `.concat()` instead of `.push()`; it returns the new array. Anyway, the most important thing here is to understand that the error means that `this.state.elements` is no longer an Array, and to debug it, you need to find the line that causes this. If you don't learn how to read error messages, you'll keep getting stuck like this. –  Jan 04 '19 at 07:19

3 Answers3

2

Change

this.state.elements.push({
    element1 : this.elementInput.value ,
    element2 : this.elementInput_2.value
})

to

[...this.state.elements, {
    element1 : this.elementInput.value ,
    element2 : this.elementInput_2.value
}]

In your addItems code, you're assigning the output of Array#push to elements. It returns the length of the new array, not the new array.

As rightly mentioned in the comment below, if you need to access previous state, is recommended to use the callback way of setting state, like

this.setState(prevState => ({
  elements: [
    ...prevState.elements,
    {
      /* New object */
    }
  ]
}));
maazadeeb
  • 5,922
  • 2
  • 27
  • 40
1

Your problem lies here when you set state

this.setState({
    elements : this.state.elements.concat([{
      element1 : this.elementInput.value ,
      element2 : this.elementInput_2.value
    }])
  })

push returns the new length of the array, so state.elements is no longer an array.

You can use spread to do this instead. I also used the callback pattern to use previous state.

this.setState(st => {
    return {
      elements : [  
        ...st.elements, 
        {
          element1 : this.elementInput.value ,
          element2 : this.elementInput_2.value
        }
      ]  
    }
  })

Another way is using concat

elements : st.elements.concat(
     {
       element1 : this.elementInput.value ,
       element2 : this.elementInput_2.value
     }))
Kevin Welch
  • 1,488
  • 1
  • 9
  • 18
0

Here you mutate the reference of this.state.elements with the function push and it's return the next length of the array.

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/push

To solve your problem you can use concat function:

addItems() {     
  this.setState({
    elements : this.state.elements.concat([{
      element1 : this.elementInput.value ,
      element2 : this.elementInput_2.value
    }])
  })
}
mickaelw
  • 1,453
  • 2
  • 11
  • 28