Bug: onChange handler is lost between re-renders

This issue has been created since 2022-09-16.

React version: 18
In our application, we have a checkbox and a button on a page. The button opens a popup which in turn attaches a onClick event of type capture at body level.
Now, when we click on the checkbox we observed the onChange handler never gets a chance to execute.

Steps To Reproduce

  1. Click on the button to open a popup.
  2. popup attaches a click handler at body level. This event is of type 'capture' to close the popup whenever some random click happens anywhere on the page.
  3. Now, if we click on the checkbox the body level click event is executed before the checkbox's onChange event.
  4. This click event in turn updates some state, which causes re-render of the checkbox component.
  5. popup is closed.
  6. The onChange event attached with the checkbox never got a chance to be executed. It is lost somewhere.

Link to code example:

The current behavior

The checkbox remains either checked or unchecked.

The expected behavior

The checkbox should get checked and unchecked with every other click.

umerprogramm wrote this answer on 2022-09-16

Hey @subha84 I'm interested to work on it
So right now, you are working on it or not?

subha84 wrote this answer on 2022-09-16

Yes, right now working on it. @umerprogramm

subha84 wrote this answer on 2022-09-17

@eps1lon @gaearon Any ideas ?

LuBustos wrote this answer on 2022-09-17

Hey @subha84! I saw you error in the codesanbox if you remove the third option in the event listener method, it seems to work.
The useEffect should look like this:

  useEffect(() => {
    let onclick = function (event) {
      setRandom(Math.floor(Math.random() * 10));
    };
    document.addEventListener("click", onclick);
    return function () {
      document.removeEventListener("click", onclick);
    };
  }, []);
riamahajan08 wrote this answer on 2022-09-19

Hey @subha84! I saw you error in the codesanbox if you remove the third option in the event listener method, it seems to work. The useEffect should look like this:

  useEffect(() => {
    let onclick = function (event) {
      setRandom(Math.floor(Math.random() * 10));
    };
    document.addEventListener("click", onclick);
    return function () {
      document.removeEventListener("click", onclick);
    };
  }, []);

@LuBustos What would be an alternative if the event needs to be processed in the capture phase?

ujjawalD07 wrote this answer on 2022-09-22

Is anyone working on this issue? I am new to open source contribution and I want to give this a go.

gaearon wrote this answer on 2022-09-22

I don't see any button in the reproducing case. How do I follow these instructions?

gaearon wrote this answer on 2022-09-22

OK, so the button in the description seems irrelevant. The issue I'm seeing is that commenting out setRandom call makes the checkbox work. So the setRandom call somehow breaks the checkbox.

I think this looks like a bug, though I'm not sure if it's fixable. I assume what happens here is:

  • Suppose controlled checked is false in the beginning.
  • When you click, the document capture level event fires.
  • The document capture level event handler updates the random state, re-rendering the component with checked={false}. This updates the controlled checkbox to have node.checked = false, even though it was true just before.
  • By the time React handles the checkbox change event, node.checked is false. So setChecked(false) keeps it unchecked.

Intuitively I'd expect that the issue is with the bold step — I think a re-render shouldn't have forced the controlled value to change if we haven't had a chance to run React handlers yet. But I'm not sure if this is something we can fix. Someone would need to dig deeper into this.

Is anyone working on this issue? I am new to open source contribution and I want to give this a go.

You're always welcome to try — note this might be a difficult one since you need to figure out how controlled inputs are implemented, and which assumption is being broken here.

subha84 wrote this answer on 2022-09-24

OK, so the button in the description seems irrelevant. The issue I'm seeing is that commenting out setRandom call makes the checkbox work. So the setRandom call somehow breaks the checkbox.

I think this looks like a bug, though I'm not sure if it's fixable. I assume what happens here is:

  • Suppose controlled checked is false in the beginning.
  • When you click, the document capture level event fires.
  • The document capture level event handler updates the random state, re-rendering the component with checked={false}. This updates the controlled checkbox to have node.checked = false, even though it was true just before.
  • By the time React handles the checkbox change event, node.checked is false. So setChecked(false) keeps it unchecked.

Intuitively I'd expect that the issue is with the bold step — I think a re-render shouldn't have forced the controlled value to change if we haven't had a chance to run React handlers yet. But I'm not sure if this is something we can fix. Someone would need to dig deeper into this.

Is anyone working on this issue? I am new to open source contribution and I want to give this a go.

You're always welcome to try — note this might be a difficult one since you need to figure out how controlled inputs are implemented, and which assumption is being broken here.

@gaearon exactly, setRandom is just simulate the use case to re-render the checkbox before the 'onChange' handler gets a chance to execute. We debugged into React code and we found the same reason you stated above, If we stop the re-render thinks work. We still haven't figured out the way to fix it considering the way the whole event system is designed. @riamahajan08

smchinna wrote this answer on 2022-09-27

@gaearon and @subha84
I have a few thoughts on this

  • Add Event listener function executed on capturing phase (document → html → body → parent → child) because of the third params passed as true on the event listener function - it won't wait to execute state updates.
  • It can resolve if executed in the bubbling phase. like
    document.addEventListener("click", onclick, false);
More Details About Repo
Owner Name facebook
Repo Name react
Full Name facebook/react
Language JavaScript
Created Date 2013-05-24
Updated Date 2022-09-27
Star Count 195296
Watcher Count 6655
Fork Count 40431
Issue Count 1113

YOU MAY BE INTERESTED

Issue Title Created Date Comment Count Updated Date
系统托盘切换 mode 后,界面不能及时响应 3 2022-09-24 2022-09-26
clash-meta 内核不能初始化 geoip.dat 模式 4 2022-09-26 2022-09-27
Trying to display non-existing printer/class in web ui doesn't return 404. 0 2022-06-27 2022-07-22
custom usercode with lpoptions isn't shown in Libreoffice 8 2022-05-30 2022-07-22
Canon PIXMA MX925: "lpadmin -p testq ... -m everywhere" returns "Unable to create PPD file: No such file or directory" 5 2022-05-30 2022-07-22
CUPS 2.4.2 creates new printers with "Option print-color-mode monochrome" as default 7 2022-06-23 2022-07-22
rpk: config bootstrap is duplicated 1 2022-07-20 2022-08-28
Start: webdav --config webdav.config isn't reading the config file 2 2021-06-16 2022-09-26
Problem package Seurat with R 4.2.1 2 2022-08-10 2022-09-03
command not working on feather client 7 2022-09-17 2022-09-19
Meteor Client crashes on Quilt Loader after dev-build 1588 1 2022-09-17 2022-09-19
Information about Virtual Machines removed from testnets.cardano.org 5 2022-07-28 2022-09-11
Allow comments in the artifact version key rules files 3 2022-03-24 2022-09-12
Suggestion: accept full paths in filename input field 1 2022-03-22 2022-09-11
使用自定义的metadata替换mysql的catalog信息,来生成数据 5 2022-02-18 2022-09-19
Startup instructions don't match the view: "Create Remote Node" 1 2022-03-29 2022-08-29
hl2lostcoast-1.1.0 fails on linux; hl2lostcoast needs to invoke hl2.sh rather than hl2_linux 1 2022-01-02 2022-09-26
Address bar showing undefined when changing to certain navigation 0 2021-06-09 2022-09-22
Infinite spinner + exceptions when trying to join a space 3 2022-01-10 2022-09-04
rawhide: aarch64: 20210827: ostree.hotfix test failing 27 2021-08-27 2022-09-04
Freecad hangs when trying to import a step file 0 2022-02-07 2022-08-10
C2C feature testing and implementation 0 2021-12-15 2022-08-08
Excluding empty values 0 2021-12-11 2022-08-28
Implement ABI changes 1 2021-12-09 2022-08-08
Publish extension on OpenVSX 2 2021-03-23 2022-08-24
Not the case with Potree 3 2020-03-09 2022-08-08
Is picocli expanding wildcards on Windows? 6 2022-07-30 2022-09-15
[Order Details] Rely on needsProcessing property when checking isEligibleForPayment 0 2022-07-16 2022-09-21
table name is confused if both flags and property file set it 1 2022-08-23 2022-09-16
Pipeline eval breaks when node has empty answers list in Tutorial 15 TableQA 1 2022-09-05 2022-09-16
Create a requirements.txt for python packages needed for ACRN developers 1 2022-08-11 2022-09-11
CTPPS ME merging problems in Harvesting step 5 2022-08-04 2022-08-15
DQM pages for bin-by-bin comparisons in PR tests come up empty 12 2022-08-05 2022-08-15
Anonymous namespace in CTPPSPixelIndices.h 8 2022-08-05 2022-08-15
Zephyr was unable to find the toolchain. Is the environment misconfigured? 0 2021-10-01 2022-09-05
Middleware composition 3 2021-01-12 2022-08-28
subtotal_with_discount includes shipping discount 27 2021-11-16 2022-08-28
support for multiple rbac configmaps 0 2022-01-31 2022-09-15
Configure test workflows to run the right tests 5 2022-08-08 2022-09-13
Automated method to determine required permissions for SDK calls for least privilege purposes 8 2022-05-22 2022-09-18
Improve version comparison in check_schema_version util function 4 2022-06-28 2022-09-25
Is it possible to check empty input values and change them with default within the ${} expression? 1 2022-03-29 2022-09-13
recalculateColumnWidths does not work at second entry to the view 0 2022-01-13 2022-08-28
Add-on Store does not show install state 4 2022-06-24 2022-09-07
Latest Update 2022.06.0 Not Starting 15 2022-06-23 2022-09-15
Home Assistant Google Drive Back error with supervisor 2022.06.2 3 2022-07-06 2022-09-26
Activity com.auth0.android.provider.AuthenticationActivity has leaked ServiceConnection com.auth0.android.provider.CustomTabsController 8 2021-02-19 2022-08-28
example of usage of alert_strategy for google_monitoring_alert_policy 5 2022-07-04 2022-09-23
RuntimeError: Did not receive SAML Response after successful authentication 1 2022-05-18 2022-09-05
Parser doesn't reject functions with missing parenthesis 0 2021-08-26 2022-09-23