Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Open
yf-yang opened this issue Oct 11, 2024 · 2 comments
Labels
Status: Unconfirmed A potential issue that we haven't yet confirmed as a bug

Comments

@yf-yang
Copy link

yf-yang commented Oct 11, 2024

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.

Screen.Recording.2024-10-18.at.10.50.40.mov

The expected behavior

Work loop stuff should not affect DOM manipulation.

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

@yf-yang yf-yang added the Status: Unconfirmed A potential issue that we haven't yet confirmed as a bug label Oct 11, 2024
@adi-ydv-1
Copy link

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

@yf-yang
Copy link
Author

yf-yang commented Oct 18, 2024

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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Status: Unconfirmed A potential issue that we haven't yet confirmed as a bug
Projects
None yet
Development

No branches or pull requests

2 participants