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: setState in onFocus breaks onChange event in Concurrent Mode #19385

Closed
airhorns opened this issue Jul 16, 2020 · 13 comments
Closed

Bug: setState in onFocus breaks onChange event in Concurrent Mode #19385

airhorns opened this issue Jul 16, 2020 · 13 comments

Comments

@airhorns
Copy link

airhorns commented Jul 16, 2020

Using concurrent mode on 0.0.0-experimental-7f28234f8 makes <Checkbox/> from Baseweb stop firing onChange events.

Baseweb is a popular set of React components that reflects real world usage patterns such that maybe it should work right off the bat with Concurrent mode. Feel free to close if you're confident this is a userland problem and not a framework problem, but I think this may indicate an incompatibility between Concurrent Mode and Legacy mode that isn't documented or warned about it if so, because <Checkbox/> isn't doing anything too fancy and doesn't use deprecated APIs or warn when rendered in Strict mode or Concurrent mode. I feel like that means it's supposed to just work, right?

React version: 0.0.0-experimental-7f28234f8

Steps To Reproduce

  1. Click a <Checkbox/> rendered using Legacy mode and note that it checks
  2. Click a <Checkbox/> rendered using Concurrent mode and note that it does not check

See issue here: https://baseweb-react-concurrent-checkbox.stackblitz.io (edit here: https://stackblitz.com/edit/baseweb-react-concurrent-checkbox?file=src/index.js)

I've been trying to debug and haven't found any smoking guns yet, but there are some interesting tidbits I can share that might allude to if this is a React bug or a Baseweb bug:

  • onFocus and onBlur handlers fire just fine on both modes
  • <Checkbox/> doesn't use findDOMNode or other deprecated APIs in Concurrent mode as best I can tell. No warnings are fired when rendering it in Strict mode or Concurrent + Strict mode
  • focusing the Checkbox by tabbing and pressing space checks and fires the onChange handler on both Legacy and Concurrent mode
  • Wide swaths of the rest of baseweb work just fine in Concurrent mode

See https://github.com/uber/baseweb/issues/3476 for the baseweb issue.

@airhorns airhorns added the Status: Unconfirmed A potential issue that we haven't yet confirmed as a bug label Jul 16, 2020
@gaearon
Copy link
Collaborator

gaearon commented Jul 16, 2020

Concurrent mode isn't supposed to require any code changes to work

This sounds a bit like an oversimplification. Concurrent Mode has several differences in behavior (some of them would still change before the release) so it's not expected that 100% of the code would work. Otherwise it wouldn't be a mode.

That said, if you can reduce this to a small repro case that doesn't involve third-party libraries, we can take a close look. I don't think it's going to be scalable for us to look at every single issue from every single third-party library. So far, there hasn't been conclusive evidence that there is a bug in React based on what you wrote.

@gaearon
Copy link
Collaborator

gaearon commented Jul 16, 2020

So the best way to diagnose this is to copy and paste Checkbox code into your sandbox, and then start removing things until you find the minimal possible repro.

@airhorns
Copy link
Author

That makes sense -- I will try to isolate and find a repro that proves it's a React issue, will close for now.

@airhorns
Copy link
Author

Ok, I think I have a pretty minimal reproduction that shows the same issue when using plain ole React here:

https://stackblitz.com/edit/react-concurrent-checkbox?file=src%2FCheckbox.js

The same <Checkbox/> component is rendered 3 times in 3 different roots, in each of Concurrent, Blocking and Legacy mode. React only seems to fire the onChange handler in Legacy mode.

My understanding of the internals of the React event system is limited, but I know that it is trying to dispatch the click event, but I get kind of lost trying to follow the stack of where that dispatch goes in Legacy mode that it doesn't in Concurrent. Would love some assistance debugging.

@airhorns airhorns reopened this Jul 16, 2020
@gaearon gaearon added Component: Concurrent Features Type: Needs Investigation and removed Status: Unconfirmed A potential issue that we haven't yet confirmed as a bug labels Jul 16, 2020
@aweary
Copy link
Contributor

aweary commented Jul 16, 2020

I took a brief look, and the issue seems to stem from onFocus setting state. Here's a further reduced example that reproduces the problem. The first time you click on that "fake" checkbox, nothing happens. If you remove the setFocused call in onFocus it works as expected.

@gaearon
Copy link
Collaborator

gaearon commented Jul 16, 2020

Wonder if this is related to #19290 or #19078.

@gaearon gaearon changed the title Bug: Concurrent Mode breaks baseweb's Checkbox component Bug: setState in onFocus breaks onChange event in Concurrent Mode Jul 16, 2020
@gaearon
Copy link
Collaborator

gaearon commented Aug 6, 2020

Just wanted to follow up that we have some related changes planned in this area in the next months to simplify the model, so we'll be looking at this bug after those changes.

@gaearon
Copy link
Collaborator

gaearon commented Sep 23, 2020

I wanted to note that if you do

  onFocus = e => {
    ReactDOM.flushSync(() => {
      this.setState({ isFocused: true });
      this.props.onFocus(e);
    });
  };

this fixes the problem as a temporary workaround.

@airhorns
Copy link
Author

👋 any update on this bug? I know it's prerelease software and it would be unreasonable to have any expectations, but, I have hacked in the flushSync fix to my UI library and we're still experiencing some similar issues. I don't think the workaround fully works around the problem, though it did improve things in a number of situations. I will continue hunting and make sure that it isn't just a missed application of the workaround.

@eps1lon
Copy link
Collaborator

eps1lon commented Nov 27, 2020

Closing in favor of #18591 which describes the same issue. Current workaround is explained in #18591 (comment). Test for this bug can be found in #19617.

@eps1lon eps1lon closed this as completed Nov 27, 2020
@gaearon
Copy link
Collaborator

gaearon commented Feb 10, 2021

This appears to be fixed with some flags we're planning to enable. Here's the sandbox demonstrating it (note it's built from #20792 rather than experimental because those flags aren't yet enabled in an experimental npm release).

https://codesandbox.io/s/samc-focus-demo-forked-7mbkl?file=/src/App.js

CM version looks fixed.

There's still an issue with the Blocking Mode version, but we'll be addressing it separately in a different way.

@gaearon
Copy link
Collaborator

gaearon commented Feb 10, 2021

Here's the reduced sandbox, also fixed when the same flags are enabled.

https://codesandbox.io/s/samc-focus-demo-forked-8znoo?file=/src/App.js

@gaearon
Copy link
Collaborator

gaearon commented Mar 31, 2021

The latest experimental release has the fix.

Also, Blocking Mode was deleted altogether so that part is solved too.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

4 participants