Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

react componentdidupdate provokes infinite loop

I trigger an API call to elasticsearch with onChange so that I can prompt a list for autocomplete. To ensure that my store was updated before rerender I added componentDidMount so that I am not one tick behind. But getting to the point needs code, so:

constructor(props) {
  super(props)
  this.state = {
    inputValue: '',
    options: []
  };
  this.props = {
    inputValue: '',
    options: []
  };
  this._onChange = this._onChange.bind(this);
}

componentDidMount() {
  CodeStore.addChangeListener(this._onChange);
}

_onChange(event) {
  if (this.state.inputValue && !event) {
    var enteredChars = this.state.inputValue;
    console.log('NO EVENT!');
  } else if (event.target.value) {
    console.log('EVENT!');
    var enteredChars = event.target.value;
    this.setState({inputValue: enteredChars});
  }
  CodeService.nextCode(enteredChars);
}

As you can see I added some log events just to get sure my condition is doing right. I read about that setState provokes a rerender so it is inside the condition but that didn't had stopped the loop . And the console log confirms the condition switch. But having the setState inside my condition brakes the functionality and I do not get a list.

Here is my log:

                        0
Home.jsx:48             EVENT!
Home.jsx:50             0
CodeService.js:27       request
CodeActions.js:10       dispatch
CodeStore.js:22         store
Home.jsx:43             1
Home.jsx:46             NO EVENT!
10OptionTemplate.jsx:15 render-list
CodeService.js:27       request
CodeActions.js:10       dispatch
CodeStore.js:22         store
Home.jsx:43             1
Home.jsx:46             NO EVENT!
10OptionTemplate.jsx:15 render-list
CodeService.js:27       request
CodeActions.js:10       dispatch
CodeStore.js:22         store
Home.jsx:43             1
Home.jsx:46             NO EVENT!

The infinity loop does not hurt the performance anyhow. I think because of componentWillUnmount but the massive amount of API calls have to be avoided. Hope this is enough information for any evidence.

like image 368
bdart Avatar asked Dec 11 '25 23:12

bdart


2 Answers

Looks like the infinite loop is caused by the following pattern:

  1. user types input
  2. _onChange updates state + fires action to store
  3. Store updates itself and send change event
  4. Store change event fires same _onChange event
  5. _onChange does not update state, but fires action to store nonetheless
  6. Goto step 3. and repeat (indefinitely)

Steps to take to fix:

  • make different change handlers for user input and store update (as already suggested by @Janaka Stevens)
  • put the data from store in state
  • user input does not have to be in state (if your react code does not provide a value to an input field, it will start with empty value, and leave whatever is typed alone)

Then your code could look something like this:

constructor(props) {
  super(props)
  this.state = {
    options: []
  };
  // removed the this.props bit: props should not be updated inside component
  this._onChangeUser = this._onChangeUser.bind(this);
  this._onChangeStore = this._onChangeStore.bind(this);
}

componentDidMount() {
  CodeStore.addChangeListener(this._onChange);    // this is OK
}

_onChangeUser(event) {
  console.log('EVENT!');
  var enteredChars = event.target.value;
  CodeService.nextCode(enteredChars);
  // No need to update state here - go to sleep until store changes
}

_onChangeStore() {
  var newList = getListFromStore();  // your own function to get list from store
  this.setState({ options: newList});            // update state here
}

Not sure what you meant by 'one tick behind', or what could have caused it, but cleaning up endless loop is a good place to start ;)

PS: Code above is sketch only, may have typo's.

like image 132
wintvelt Avatar answered Dec 13 '25 11:12

wintvelt


It looks like you are using _onChange for both your input event and store listener. They need separate methods.

like image 22
J. Mark Stevens Avatar answered Dec 13 '25 12:12

J. Mark Stevens