React-number-format: Changing value and suffix at the same time causes Maximum update depth exceeded error

Created on 30 Jan 2019  路  15Comments  路  Source: s-yadav/react-number-format

I'm using react-number-format in combination with Formik and material-ui. I have a use case when I need to call Formiks resetForm and it updates value and a suffix at the same time. E.g. (100 m to 0 ft ).

const NumberFormatCustom = (props: *): NumberFormat => { const { inputRef, onChange, unit, ...other } = props; return ( <NumberFormat {...other} getInputRef={inputRef} onValueChange={values => { onChange({ target: { value: values.value, name: other.name, }, }); }} suffix={unit} /> ); };

NumberFormat is passed though InputProps to TextField.

InputProps={{ inputComponent: NumberFormatCustom }}

This is the console log that shows Maximum update depth exceeded error.

screen shot 2019-01-30 at 5 15 00 pm

Most helpful comment

I think the problem comes from L134 in <NumberFormat>:
https://github.com/s-yadav/react-number-format/blob/81ef1cfe38cd06b50e1e9753ff956abefead4da4/src/number_format.js#L134

When a component is re-rendered, the props and prevProps will not be the same instance, so this expression will always be true?

I've noticed a situation when a user types really fast, there is a chance that this component gets re-rendered (for any reason):

  • after it calls setState() inside updateValue() (triggered from onChange), but
  • before the setState() callback is called (so a ancestor component gets notified of new value)

And thus the component is rendered with old value when it's controlled, and it always override its state even when the value in state is actually newer.

After that the callback is finally fired, bringing value to its ancestor component, and triggered another re-render. In my case this is where the whole thing goes into an infinite loop.

All 15 comments

I was getting something similar -- "Maximum update depth..." message when a phone number field was autofilled by Chrome.

I updated the onValueChange() function to check for the existence of the values object as well as the values.formattedValue key before calling onChange. This appears to have solved my problem. Hope this helps.

@rykon I did the same few days ago and it helps. Thank you. But still, it should be fixed in react-number-format.

It looks like checking to see whether or not formattedValue is truthy before calling onValueChange() on line #695 would fix the problem.

checking formattedValue is truthy before calling onValueChange is not correct as when user press removes all the value in the input. The empty value has to be passed to the onValueChange method.

Will check for the root cause.

@AlminaHaskic @rykon Changing value and suffix at the same time works on the normal case. Can you create a codesandbox (https://codesandbox.io) with failing case. So it will be easier to debug.

I think I found a workaround by using

value={this.state.value || undefined}

This briefly switches the component to uncontrolled if there鈥檚 now valid input and breaks the infinite loop.

For me the problem is happening when I set name and value at onChange key:

<NumberFormat
      {...other}
      onValueChange={values => {
        onChange({
          target: {
            value: values.value,
            name: other.name,
          },
        });
      }}
      thousandSeparator="."
      decimalSeparator=","
      decimalScale={2}
      fixedDecimalScale
      prefix="R$"
    />

if (oldValue === newValue) return before updating a state inside onValueChange helped me.

I think I found a workaround by using

value={this.state.value || undefined}

This briefly switches the component to uncontrolled if there鈥檚 now valid input and breaks the infinite loop.

This works for me. We found it by holding the backspace key down.

If someone can create a codesandbox with failing case. It will be easier for me to debug.

I think the problem comes from L134 in <NumberFormat>:
https://github.com/s-yadav/react-number-format/blob/81ef1cfe38cd06b50e1e9753ff956abefead4da4/src/number_format.js#L134

When a component is re-rendered, the props and prevProps will not be the same instance, so this expression will always be true?

I've noticed a situation when a user types really fast, there is a chance that this component gets re-rendered (for any reason):

  • after it calls setState() inside updateValue() (triggered from onChange), but
  • before the setState() callback is called (so a ancestor component gets notified of new value)

And thus the component is rendered with old value when it's controlled, and it always override its state even when the value in state is actually newer.

After that the callback is finally fired, bringing value to its ancestor component, and triggered another re-render. In my case this is where the whole thing goes into an infinite loop.

I've noticed a situation when a user types really fast

This is what clued us in on this error - our tests were causing this loop because they type fast. Turns out a user can also trigger it. Would a better equality check fix the situation?

Memoizing NumberFormat with our own equality check seems to fix it, but I'm a bit hesitant about the side-effects of this - you probably want to also check on other passed components that might change.

const MemoNumberFormat = memo(NumberFormat, (prev, next) => {
  return prev.value === next.value;
});

I've proposed a possible fix over #370. Please let me know if that helps!

Sorry for looking into this late. Will get the fix pushed ASAP.

There is a probable fix in 4.4.1.
Closing the issue. Will reopen if the issue persists.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

osvaldokalvaitir picture osvaldokalvaitir  路  3Comments

havantuan picture havantuan  路  7Comments

sezarthiago picture sezarthiago  路  7Comments

Krakof picture Krakof  路  6Comments

bernatfortet picture bernatfortet  路  3Comments