David
David

Reputation: 25

React state array issue

I wanted to store array objects in react state. I don't know what I'm missing but my code is not working. Here is my code below :

export default class Groups extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      tabledata: []
    }
  }

  componentDidMount() {
    this.setState({
      tabledata: [...this.state.tabledata, {"name": "name1"}]
    })

    console.log("HI")
    console.log(this.state.tabledata)
  }
}

The console log return empty state not storing anyting in state array.. Please suggest what's wrong here. Thank you in advance..

Upvotes: 0

Views: 159

Answers (2)

Mohammad Abedy
Mohammad Abedy

Reputation: 60

You can push the object(s) into the array:

    export default class Groups extends React.Component {
    constructor(props) {
      super(props);
      this.state = {
        tabledata: []
      }
    }

    componentDidMount() {
      this.setState({
        // tabledata: [...this.state.tabledata, {"name": "name1"}]
            tabledata: this.state.tabledata.push({"name": "name1"})
      })

      console.log("HI")
      console.log(this.state.tabledata)
    }

Upvotes: 1

T.J. Crowder
T.J. Crowder

Reputation: 1075457

There are two separate problems there, but both spring from the same cause: State updates are asynchronous.

So the two problems are:

  1. You're setting state based on existing state by passing an object into setState. That could set stale state. When updating state based on existing state, you must use the callback version of setState.

  2. You're logging state immediately after calling setState. That won't see the updated state, because the update is asynchronous. Instead, log it in the completion callback (if you need to do something with it; normally, you don't need this, just allow React to re-render, which it will do by default).

Here are those corrections applied to your code:

export default class Groups extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      tabledata: []
    }
  }

  componentDidMount() {
    this.setState(
      // Update callback receives the current state. Here I'm picking
      // `tabledata` from it via destructuring
      ({tabledata}) => ({
        tabledata: [...tabledata, {"name": "name1"}]
      }),
      // Completion callback
      () => {
        console.log("HI")
        console.log(this.state.tabledata)
      }
    )
  }
}

Live Example:

class Groups extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      tabledata: []
    }
  }

  componentDidMount() {
    this.setState(
      ({tabledata}) => ({
        tabledata: [...tabledata, {"name": "name1"}]
      }),
      () => {
        console.log("HI")
        console.log(this.state.tabledata)
      }
    )
  }
  
  render() {
    return this.state.tabledata.map(({name}) => <div>{name}</div>);
  }
}

ReactDOM.render(<Groups />, document.getElementById("root"));
<div id="root"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.6/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.6/umd/react-dom.production.min.js"></script>

Again, though, normally you don't need or want to use the completion callback, so:

export default class Groups extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      tabledata: []
    }
  }

  componentDidMount() {
    this.setState(({tabledata}) => ({
      tabledata: [...tabledata, {"name": "name1"}]
    }));
  }
}

Upvotes: 1

Related Questions