React: duplicated item in list

Viewed 1032

I'm an absolute beginner in React. I created a simple app to show an integer list on a page. There is an 'add item' button and the app happens a new integer to the list when user presses the button. When I press the button, the new integer is added twice to the list. Can anyone help?

import React from 'react';

class App extends React.Component {
  constructor(props) {
    super(props);

    this.state = {
      items: [],
      idx: 0
    }

    this.addItem = this.addItem.bind(this);
  }

  render() {
    return <div><h1>Learning app</h1>
    <ul>
      {this.state.items.map(item => (
        <li key={item}>{item}</li>
      ))}
    </ul>
    <button onClick={this.addItem}>Add Item</button>
    </div>;
  }

  addItem() {
    this.setState((state, props) => {
      state.items.push(state.idx)

      return {
        items: state.items,
        idx: state.idx + 1
      }
    })
  }
}

export default App;

When I put this inside addItem

    this.state.items.push(this.state.idx);
    this.setState({
      idx: this.state.idx + 1
    })

it works without any issue.

I deliberately use setState with arrow function for experiment purpose. Did I do something wrong?

3 Answers

You can try this, it will add a single item in the list

addItem = () => {
    this.setState({
      items: [...this.state.items, this.state.idx],
      idx: this.state.idx + 1,
    });
  };

There is nothing wrong in using callback approach with setState.

The issue is you are mutating the the previous state array by doing .push. Let's keep in mind that with strict mode, react executes setState twice. So you are kind of mutating the prev state array twice.

So use spread operator

addItem() {
    this.setState((state, props) => {
      return {
        items: [...state.items, state.idx],
        idx: state.idx + 1
      };
    });
  }

Working demo

I would do it this way.

import React from "react";

class App extends React.Component {
  constructor(props) {
    super(props);

    this.state = {
      items: [],
      idx: 0
    };
  }

  render() {
    return (
      <div>
        <h1>Learning app</h1>
        <ul>
          {this.state.items.map(item => (
            <li key={item}>{item}</li>
          ))}
        </ul>
        <button onClick={this.addItem}>Add Item</button>
      </div>
    );
  }

  addItem = () => {
    this.setState({
      idx: this.state.idx + 1,
      items: [...this.state.items, this.state.idx]
    });
  };
}

export default App;
Related