Skip to content

Commit f36122c

Browse files
committed
fix: Don't trigger "missing act" warnings when using waitFor+real timers
1 parent c888cb6 commit f36122c

File tree

4 files changed

+93
-6
lines changed

4 files changed

+93
-6
lines changed

src/__tests__/end-to-end.js

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,6 @@ function ComponentWithLoader() {
1717
let cancelled = false
1818
fetchAMessage().then(data => {
1919
if (!cancelled) {
20-
// Will trigger "missing act" warnings in React 18 with real timers
21-
// Need to wait for an action on https://github.com/reactwg/react-18/discussions/23#discussioncomment-1087897
2220
setState({data, loading: false})
2321
}
2422
})

src/act-compat.js

Lines changed: 68 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,73 @@ function actPolyfill(cb) {
1515
ReactDOM.render(<div />, document.createElement('div'))
1616
}
1717

18-
const act = isomorphicAct || domAct || actPolyfill
18+
function getGlobalThis() {
19+
if (typeof self !== 'undefined') {
20+
return self
21+
}
22+
if (typeof window !== 'undefined') {
23+
return window
24+
}
25+
if (typeof global !== 'undefined') {
26+
return global
27+
}
28+
throw new Error('unable to locate global object')
29+
}
30+
31+
function setReactActEnvironment(isReactActEnvironment) {
32+
getGlobalThis().IS_REACT_ACT_ENVIRONMENT = isReactActEnvironment
33+
}
34+
35+
function getIsReactActEnvironment() {
36+
return getGlobalThis().IS_REACT_ACT_ENVIRONMENT
37+
}
38+
39+
function withGlobalActEnvironment(actImplementation) {
40+
return callback => {
41+
const previousActEnvironment = getIsReactActEnvironment()
42+
setReactActEnvironment(true)
43+
try {
44+
// The return value of `act` is always a thenable.
45+
let callbackNeedsToBeAwaited = false
46+
const actResult = actImplementation(() => {
47+
const result = callback()
48+
if (
49+
result !== null &&
50+
typeof result === 'object' &&
51+
typeof result.then === 'function'
52+
) {
53+
callbackNeedsToBeAwaited = true
54+
}
55+
return result
56+
})
57+
if (callbackNeedsToBeAwaited) {
58+
const thenable = actResult
59+
return {
60+
then: (resolve, reject) => {
61+
thenable.then(
62+
returnValue => {
63+
setReactActEnvironment(previousActEnvironment)
64+
resolve(returnValue)
65+
},
66+
error => {
67+
setReactActEnvironment(previousActEnvironment)
68+
reject(error)
69+
},
70+
)
71+
},
72+
}
73+
} else {
74+
setReactActEnvironment(previousActEnvironment)
75+
return actResult
76+
}
77+
} catch (error) {
78+
setReactActEnvironment(previousActEnvironment)
79+
throw error
80+
}
81+
}
82+
}
83+
84+
const act = withGlobalActEnvironment(isomorphicAct || domAct || actPolyfill)
1985

2086
let youHaveBeenWarned = false
2187
let isAsyncActSupported = null
@@ -131,6 +197,6 @@ function asyncAct(cb) {
131197
}
132198

133199
export default act
134-
export {asyncAct}
200+
export {asyncAct, setReactActEnvironment, getIsReactActEnvironment}
135201

136202
/* eslint no-console:0 */

src/pure.js

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,11 @@ import {
55
prettyDOM,
66
configure as configureDTL,
77
} from '@testing-library/dom'
8-
import act, {asyncAct} from './act-compat'
8+
import act, {
9+
asyncAct,
10+
getIsReactActEnvironment,
11+
setReactActEnvironment,
12+
} from './act-compat'
913
import {fireEvent} from './fire-event'
1014

1115
configureDTL({
@@ -30,7 +34,18 @@ if (React.startTransition !== undefined) {
3034
unstable_advanceTimersWrapper: cb => {
3135
return act(cb)
3236
},
33-
asyncWrapper: cb => cb(),
37+
// We just want to run `waitFor` without IS_REACT_ACT_ENVIRONMENT
38+
// But that's not necessarily how `asyncWrapper` is used since it's a public method.
39+
// Let's just hope nobody else is using it.
40+
asyncWrapper: async cb => {
41+
const previousActEnvironment = getIsReactActEnvironment()
42+
setReactActEnvironment(false)
43+
try {
44+
return await cb()
45+
} finally {
46+
setReactActEnvironment(previousActEnvironment)
47+
}
48+
},
3449
})
3550
}
3651

tests/setup-env.js

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,9 @@
11
import '@testing-library/jest-dom/extend-expect'
2+
3+
beforeEach(() => {
4+
global.IS_REACT_ACT_ENVIRONMENT = true
5+
})
6+
7+
afterEach(() => {
8+
global.IS_REACT_ACT_ENVIRONMENT = false
9+
})

0 commit comments

Comments
 (0)