Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Warning: setState(…): Cannot update during an existing state transition

I'm developing a simple 'to do list' react app (new to React.js). I have adding items to a list working but deleting items raises a question. Within my parent react component, i have the following code:

import ToDoEntries from './to_do_entries.jsx';

class ToDoList extends React.Component {
  constructor(props) {
    super(props);
    this.state = { list: [] }
    this.add = this.addItem.bind(this);
    this.removeItem = this.removeItem.bind(this);
  }

  addItem(e) { //removed to avoid tl:dr }

  render() {
    return(
      <form onSubmit={this.add}>
        <input placeholder='Enter item' type='text' ref={(el) => {this._input = el;} }/>
        <button>Add</button>
      </form>

      <ToDoEntries entries={this.state.list}
        removeCallback={this.removeItem}
      />
    );
  }

}

My to_do_entries.jsx component:

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

  renderItems() {
    const { entries, removeCallback } = this.props;

    function createTasks(item) {
      return <li key={item.key}>{item.text}</li>
    }

    var listItems = entries.map(function(item) {
      return(<li onClick={removeCallback} key={item.key}>{item.text}</li>)
    })

    return listItems;
  }

  render() {
    var todoEntries = this.renderItems();

    return(
      <ul>
        {todoEntries}
      </ul>
    );
  }
}

export default ToDoEntries;

Running this code bring:

Warning: setState(…): Cannot update during an existing state transition

Question:

why does to_do_entries.jsx's render immediately execute the callback when an item gets added i.e:

var listItems = entries.map(function(item) {
  return(<li onClick={removeCallback(id)} key={item.key}>{item.text}</li>)
})

However, adding .bind(null, id) to removeCallback ie. <li onClick={removeCallback.bind(null, id)} /> does not?

like image 648
soups Avatar asked Jan 31 '23 00:01

soups


1 Answers

Problem is in this part:

onClick={removeCallback(id)}

We need to pass a function to onClick, not the value. When we use () with functionName, that means you are calling that method and assigning the result of that to onClick, that will create a infinite loop if you do setState in removeCallback, because of this cycle:

render ->  removeCallback()  ->  setState ->
  ^                                         |
  |                                         |
  |                                         |
   -----------------------------------------

That's why you are getting the error.

Check the snippet for difference between abc and abc():

function abc(){
   return 'Hello';
}

console.log('without () = ', abc);     //will return the function
 
console.log('with () = ', abc());      //will return the function result (value)

Why it is working with onClick={removeCallback.bind(null, id)}?

Because bind will create a new function, and assign that function to click event, here removeCallback will get called when you click on any item not automatically.

As per MDN Doc:

The bind() function creates a new bound function (BF). A BF is an exotic function object (a term from ECMAScript 2015) that wraps the original function object. Calling a BF generally results in the execution of its wrapped function.

Check the React DOC: Handling events in JSX.

Check this answer for more details about bind: Use of the JavaScript 'bind' method

like image 59
Mayank Shukla Avatar answered Feb 02 '23 18:02

Mayank Shukla