This.state.messages.map is not a function in React Redux Help

Tell us what’s happening:

I keep getting this error that “this.state.messages.map is not a function”

can anyone help me what is wrong with my code?

Your code so far


class DisplayMessages extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      input: '',
      messages: [],
    }

    this.handleChange = this.handleChange.bind(this);
    this.submitMessage = this.submitMessage.bind(this);
  }
  // add handleChange() and submitMessage() methods here

    handleChange(e) {
        this.setState({
          input: e.target.value
        })
    }

    submitMessage(e){
      e.preventDefault();
        this.setState({
          messages: this.state.messages.push(this.state.input),
          input: '',
        })
    }

  render() {
    return (
      <div>
        <h2>Type in a new Message:</h2>


        <input type='text' 
        onChange={this.handleChange} 
        value={this.state.input}  />

        <button onClick={this.submitMessage}> 
           Add message
        </button>
        <ul>
           {
             this.state.messages.map( (msg) => (
               <li> {msg} </li>
             )
             )
           }
        </ul>
        
      </div>
    );
  }
};

Your browser information:

User Agent is: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.100 Safari/537.36.

Link to the challenge:
https://learn.freecodecamp.org/front-end-libraries/react-and-redux/manage-state-locally-first

Array.push() returns:

the new length property of the object upon which the method was called.

This is from the MDN documentation. Array.prototype.push() - JavaScript | MDN

However, this is the less serious of mistakes that you’re making in your code. The more serious mistake is that you’re modifying state directly with your this.state.messages.push() call. This mutates this.state.messages which is what we don’t want to do.

Instead, create a copy of this.state.messages, push the input onto the copy, and set this.state.messages to said copy:

submitMessage(e){
  e.preventDefault();
  const messagesCopy = this.state.messages.slice();
  messagesCopy.push(this.state.input);
  this.setState({
    messages: messagesCopy,
    input: '',
  });
}

Good luck!

1 Like

As @WebDevCoach points out above, you could go the route of creating a copy and then pushing the the input property value into the copy, and then assigning the copy to message.

OR

Simply learn how you could use Array.prorotype.concat in your solution.

1 Like

After reading the MDN documentation and your example, I understand it now. Thank you!
.push returns the length and not the added element at the end. Also, thank you for pointing out that I am modifying state directly. I will use .concat method instead as randelDawson said.

Sir , after using the concept discussed above I am getting another error stating TypeError: Cannot read property ‘slice’ of undefined.

This is my code:


class New extends Component {

  constructor() {
    super();
    this.state = {
      type: [],
      level: []
    }
	}
	
	handleSubmit = event => {
    event.preventDefault();
    this.props.savecourseMetadata(this.state);
	}
	
	handleInputChange = event => {
    event.preventDefault();
    const typeCopy = this.state.map.slice();
    typeCopy.push(this.state.name);
    this.setState({
     type: typeCopy,
    });
  }

  addMoreType = () => {
    let type = this.state.type;
    type.push("");
    this.setState({ type: type});
  }

  removeType = () => {
    let type = this.state.type;
    type.pop("");
    this.setState({ type: type});
  }

  handleAbcChange = (event) => {
    let type = this.state.type;
    type.push("");
    this.setState({ type: type });
  }




  componentWillUpdate(nextProps, nextState) {
    if ((nextProps.courseMetadataSaved) && (!this.props.courseMetadataSaved)) {
      Router.push('/admin/course_metadata');
    }
  }



  render() {
    console.log(this.state)
    return (
      <div>
			<AdminLayout>
			  <h1> New CourseMetadata</h1>
        <form onSubmit={(event) => this.handleSubmit(event)}>

          <input
            type="type"
            placeholder="type"
            name="type"
            onChange={this.handleInputChange}
          />  

          {this.state.type.map((type, index) => {
            return (
              <div key={index}>
                <input type="text" onChange={this.handleAbcChange}></input>
              </div>
            )
          })}

          <button type="button" onClick={this.addMoreType} > Add type </button>
          <button type="button" onClick={this.removeType} > Remove type </button>

        
       
          <button type="submit" > Submit </button>
         
          
              

Think about, what you want to do here.

Hey I have one more issue with my code above. I have created an add button by clicking on which I will be getting an Input field box but when I am writing in that input field " on every character , a new input field box getting added " why this so ?

Can you help?

No code, no help :wink:

The code I provided above is the one only.

Where is:

  • the closing </form>?
  • the closing </AdminLayout>?
  • the closing </div>?
  • the whole <AdminLayout> component?

`

I got it Thank you btw.