Skip to content

Commit 7ecf525

Browse files
committed
test: current behavior of flushing updates from discrete events too late
1 parent 58150b9 commit 7ecf525

File tree

1 file changed

+48
-0
lines changed

1 file changed

+48
-0
lines changed

src/__tests__/events.js

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import * as React from 'react'
2+
import * as ReactDOM from 'react-dom'
23
import {render, fireEvent} from '../'
34

45
const eventTypes = [
@@ -254,3 +255,50 @@ test('blur/focus bubbles in react', () => {
254255
expect(handleFocus).toHaveBeenCalledTimes(1)
255256
expect(handleBubbledFocus).toHaveBeenCalledTimes(1)
256257
})
258+
259+
test('discrete events are not wrapped in act', () => {
260+
function AddDocumentClickListener({onClick}) {
261+
React.useEffect(() => {
262+
document.addEventListener('click', onClick)
263+
return () => {
264+
document.removeEventListener('click', onClick)
265+
}
266+
}, [onClick])
267+
return null
268+
}
269+
function Component({onDocumentClick}) {
270+
const [open, setOpen] = React.useState(false)
271+
272+
return (
273+
<React.Fragment>
274+
<button onClick={() => setOpen(true)} />
275+
{open &&
276+
ReactDOM.createPortal(
277+
<AddDocumentClickListener onClick={onDocumentClick} />,
278+
document.body,
279+
)}
280+
</React.Fragment>
281+
)
282+
}
283+
const onDocumentClick = jest.fn()
284+
render(<Component onDocumentClick={onDocumentClick} />)
285+
286+
const button = document.querySelector('button')
287+
fireEvent.click(button)
288+
289+
// We added a native click listener from an effect.
290+
// There are two possible scenarios:
291+
// 1. If that effect is flushed during the click the native click listener would still receive the event that caused the native listener to be added.
292+
// 2. If that effect is flushed before we return from fireEvent.click the native click listener would not receive the event that caused the native listener to be added.
293+
// React flushes effects scheduled from an update by a "discrete" event immediately.
294+
// but not effects in a batched context (e.g. act(() => {}))
295+
// So if we were in act(() => {}), we would see scenario 2 i.e. `onDocumentClick` would not be called
296+
// If we were not in `act(() => {})`, we would see scenario 1 i.e. `onDocumentClick` would already be called
297+
expect(onDocumentClick).toHaveBeenCalledTimes(1)
298+
299+
// verify we did actually flush the effect before we returned from `fireEvent.click` i.e. the native click listener is mounted.
300+
document.dispatchEvent(
301+
new MouseEvent('click', {bubbles: true, cancelable: true}),
302+
)
303+
expect(onDocumentClick).toHaveBeenCalledTimes(2)
304+
})

0 commit comments

Comments
 (0)