David Rönnlid
David Rönnlid

Reputation: 91

Display new list item onclick with ReactJS

I have a problem with updating and displaying an array as list items on the click of a button. From below code, I get error message:

list.map is not a function

import React from "react";


export default class InputPushToList extends React.Component {
  constructor(props) {
    super(props);

    this.state = {
      ActiveText: "",
      li: ["First Item"]
    };

    this.handleOnClick = this.handleOnClick.bind(this);
    this.handleChange = this.handleChange.bind(this);
  }

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

  handleOnClick(e) {
    e.preventDefault();

    const newListItem = [this.state.ActiveText];

    this.setState({
      li: this.state.li.push(newListItem)
    });
  }

  render() {
    const list = this.state.li;
    return (
      <React.Fragment>
        <h2>{this.props.inputTitle}</h2>
        <input type="text" onChange={this.handleChange} />
        <h2>{this.state.ActiveText}</h2>
        <br />
        <button onClick={this.handleOnClick}>Add to list</button>

        <ul>
          {list.map(e => (
            <li>{e}</li>
          ))}
        </ul>
      </React.Fragment>
    );
  }
}

In my mind list.map is a function but apparently not, so why? And how would you go about displaying one more (that which is the active state of the input, this.state.ActiveText) list item when clicking the button? Thanks for the help.

Upvotes: 0

Views: 6416

Answers (3)

Manu
Manu

Reputation: 10944

First thing first, you don't use this.state inside this.setState, instead use a function to update state. Check this for reference: https://reactjs.org/docs/state-and-lifecycle.html#state-updates-may-be-asynchronous

Your code should be as follows:

this.setState((state) => ({
  li: state.li.concat([newListItem])
}));

Second, why are you assigning an array to newlistitem by doing: const newListItem = [this.state.ActiveText]; ?

It should be const newListItem = this.state.ActiveText;

Upvotes: 1

Liren Yeo
Liren Yeo

Reputation: 3451

1. Don't mutate this.state

In handleOnClick(), do not write this.state.li.push(newListItem).

Instead, make a clone of this.state.li, add newListItem into that clone, and set the state of li to the new clone:

handleOnClick(e) {
  e.preventDefault();
  this.setState({
    li: [
      ...this.state.li, // spread out the existing li
      this.state.ActiveText // append this newListItem at the end of this array
    ]
  });
}

2. Consider destructuring

In your render(), you could destructure this.state.li:

render() {
  const { li } = this.state
  return (
    <React.Fragment>
      ...other stuffs
      <ul>
        {li.map(e => (
          <li>{e}</li>
        ))}
      </ul>
    </React.Fragment>
  );
}

Upvotes: 1

Ved
Ved

Reputation: 12103

Problem is this line:

this.setState({
      li: this.state.li.push(newListItem)
    });

example:

var arr = [];
console.log(arr.push({}))// it will print 1.

in your case:

this.setState({
      li: this.state.li.push(newListItem)// here you assigned `li`1.
    });

Fix the above.

Upvotes: 1

Related Questions