-
Notifications
You must be signed in to change notification settings - Fork 47.4k
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
Fix native event batching in concurrent mode #21010
Changes from 1 commit
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -281,4 +281,44 @@ describe('ReactDOMNativeEventHeuristic-test', () => { | |
expect(container.textContent).toEqual('hovered'); | ||
}); | ||
}); | ||
|
||
// @gate experimental | ||
it('should batch inside native events', async () => { | ||
const root = ReactDOM.unstable_createRoot(container); | ||
|
||
const target = React.createRef(null); | ||
function Foo() { | ||
const [count, setCount] = React.useState(0); | ||
|
||
React.useEffect(() => { | ||
Scheduler.unstable_yieldValue(count); | ||
}, [count]); | ||
|
||
React.useLayoutEffect(() => { | ||
target.current.onclick = () => { | ||
setCount(c => c + 1); | ||
// Now update again, this should be batched. | ||
setCount(c => c + 1); | ||
}; | ||
}); | ||
return <div ref={target}>Count: {count}</div>; | ||
} | ||
|
||
await act(async () => { | ||
root.render(<Foo />); | ||
}); | ||
expect(Scheduler).toHaveYielded([0]); | ||
expect(container.textContent).toEqual('Count: 0'); | ||
|
||
// Ignore act warning. We can't use act because it forces batched updates. | ||
spyOnDev(console, 'error'); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I don't love this but I cannot get toErrorDev to work. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Make sure you assert that only the expected error is logged, though expect(console.error).toHaveBeenCalledTimes(1);
expect(console.error.calls.argsFor(0)[0]).toContain(hookWarningMessage); |
||
|
||
const pressEvent = document.createEvent('Event'); | ||
pressEvent.initEvent('click', true, true); | ||
dispatchAndSetCurrentEvent(target.current, pressEvent); | ||
|
||
expect(Scheduler).toHaveYielded([]); | ||
expect(container.textContent).toEqual('Count: 2'); | ||
expect(Scheduler).toFlushAndYield([2]); | ||
}); | ||
}); |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -547,7 +547,10 @@ export function scheduleUpdateOnFiber( | |
} else { | ||
ensureRootIsScheduled(root, eventTime); | ||
schedulePendingInteractions(root, lane); | ||
if (executionContext === NoContext) { | ||
if ( | ||
(fiber.mode & ConcurrentMode) === NoMode && | ||
executionContext === NoContext | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Nit: Check |
||
) { | ||
// Flush the synchronous work now, unless we're already working or inside | ||
// a batch. This is intentionally inside scheduleUpdateOnFiber instead of | ||
// scheduleCallbackForFiber to preserve the ability to schedule a callback | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If you change these to
setCount(c + 1)
instead of using the updater form, then the final DOM output will be different depending on whether they are batched. 1 if batched, 2 if not batched.Edit: Or rather,
setCount(someRef.current + 1)
.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just a suggestion because the test failure will be more obvious. Like if you ran this in a sandbox, you wouldn't notice if there were two synchronous commits instead of one, but you would notice that it's displaying the wrong number.