osstotalsoft / rocket-ui-ts

A collection of reusable and composable React components written in TypeScript built on top of Material UI Core V5
MIT License
19 stars 4 forks source link

TextField value flickers and cuts characters when debounced #77

Closed st-angelo closed 1 year ago

st-angelo commented 1 year ago

Latest version

Current behavior

Currently, when a TextField is debounced and the onChange handler is slower (causes a lot of rerenders or has more complex logic), if the user continues typing after the debounced onChange is called, some of the characters can be lost and the TextField value can flicker, quickly switching between the old and new values.

Expected behavior

The TextField is properly debounced and the shown value always reflects what the user typed.

Steps to reproduce

The issue is reproduced in this sandbox: https://codesandbox.io/s/vigilant-roentgen-7xvvc4

To reproduce, type something, wait roughly until the debounce timeout passes, then quickly type some more. Because of the delay of 100ms set on the onChange handler, anything written during those 100ms will be overwritten.

Also attaching a gif of the problem: DebouncedInputProblem

dragos-rosca commented 1 year ago

We manage to reproduce this and noticed that the problem is caused by the fact we have a "live" value that we show on the page that is different from the property value that we receive from the parent component. This is necessary in a debounced scenario where the parent component does not have the latest inputs from the user. We noticed that your process of storing the value from the "onChange" callback and putting it back in the value property of the TextField is slow and lags behind the "live" value. This desynchronization creates a "property change" event for the TextField, with old data. The component has no way of knowing the value is outdated and because its a "request" from the parent component it treats it as a priority, changing the liveValue to the parent's request, creating the flicker and loss of data.

We suggest you use the TextField component as "uncontrolled". You can give it initial values and even change them based on external factors, but when it comes to user input, just use the onChange callbacks to trigger the business you need (like update your state).

st-angelo commented 1 year ago

After further investigation, I'm noticing an issue with the debounced function itself. It regenerates whenever onChange is modified, which, in some cases, is pretty often, because onChange might not be memoized.

It seems like the issue was not slow rerenders (even though in the reproduced - but fake, possibly not likely to occur in a real app - scenario that might happen), but that new debounced functions are being created and the old ones aren't cancelled.