react icon indicating copy to clipboard operation
react copied to clipboard

Bug: Controlled state input weird behavior when state update is postponed to the next micro task

Open yf-yang opened this issue 1 year ago • 2 comments

React version: v18.3

Steps To Reproduce

  1. Open the codesandbox link below
  2. Add characters in the middle of each input
  3. The first input works as expected. In the second input, the cursor will instantly jump to the end, also input methods will not work.

Link to code example: https://codesandbox.io/p/sandbox/74mhkd

The current behavior

By adding an additional queueMicrotask to the second input, two inputs behavior are different.

The expected behavior

Work loop stuff should not affect DOM manipulation.

Is it a bug or feature? Is it a react stuff?

yf-yang avatar Oct 11 '24 03:10 yf-yang

I have run it but haven't encountered any issues with it.

adi-ydv-1 avatar Oct 17 '24 19:10 adi-ydv-1

Investigation

Generally speaking, React 18 auto batching's mechanism of event handlers is like:

function domOnValueChange(e) {
  reactComponentOnChange(e); // collect state updates but does not actually performs those updates, not an actual function name
  performStateUpdates(); // here the auto batching works, not an actual function name

  // some additional codes to maintain DOM node states
  restoreControlledState(element, props); // actual function, use latest props value of the corresponding component to update element  
}

The restoreControlledState function will then deal with <input />, <textarea /> and <select /> specifically. It will finally call three different functions called updateWrapper that handles the DOM node.

Suppose this is an <input /> component, then when a character is typed, the DOM <input /> node will change, its value will add one character, and triggers the change event handler. After some state changes, it will finally call updateWrapper.

Here, if the state is updated in the same micro task, then the props.value property will be the same with current DOM node, so the cursor location will not be affected.

Instead, if the state update is postponed to another micro task, then props.value remains unchanged (suppose it is 'second'), but the DOM node's value has already added one more character (suppose it is 'secaond'). Here React's updateWrapper will reset the DOM node's value to the unchanged one ('secaond' -> 'second'). During the reset operation, the cursor location is lost. During the next micro task, although the state will finally be updated to the one with one more character ('secaond'), the cursor location is lost and would be moved to the last character.

Thoughts

I personally think this issue should be categorized as a wont fix, but maybe someone can offer a solution to make it more sound, I'm not sure.

NOTE: In latest React main branch some function name has changed, but their names are pretty similar and it is not difficult to find the corresponding logic.

yf-yang avatar Oct 18 '24 03:10 yf-yang

This issue has been automatically marked as stale. If this issue is still affecting you, please leave any comment (for example, "bump"), and we'll keep it open. We are sorry that we haven't been able to prioritize it yet. If you have any new additional information, please include it with your comment!

github-actions[bot] avatar Jan 19 '25 14:01 github-actions[bot]

Closing this issue after a prolonged period of inactivity. If this issue is still present in the latest release, please create a new issue with up-to-date information. Thank you!

github-actions[bot] avatar Jan 26 '25 14:01 github-actions[bot]