Skip to content

Commit ccd8a0d

Browse files
eps1lonph-fritsche
andauthored
feat: Add support for React 18 (#1031)
BREAKING CHANGE: Drop support for React 17 and earlier. We'll use the new [`createRoot` API](reactwg/react-18#5) by default which comes with a set of [changes while also enabling support for concurrent features](reactwg/react-18#4). To can opt-out of this change by using `render(ui, { legacyRoot: true } )`. But be aware that the legacy root API is deprecated in React 18 and its usage will trigger console warnings. Co-authored-by: Philipp Fritsche <ph.fritsche@gmail.com>
1 parent 0c4aabe commit ccd8a0d

File tree

14 files changed

+370
-444
lines changed

14 files changed

+370
-444
lines changed

.github/workflows/validate.yml

+3
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ jobs:
1616
# ignore all-contributors PRs
1717
if: ${{ !contains(github.head_ref, 'all-contributors') }}
1818
strategy:
19+
fail-fast: false
1920
matrix:
2021
# TODO: relax `'16.9.1'` to `16` once GitHub has 16.9.1 cached. 16.9.0 is broken due to https://github.com/nodejs/node/issues/40030
2122
node: [12, 14, '16.9.1']
@@ -52,6 +53,8 @@ jobs:
5253

5354
- name: ⬆️ Upload coverage report
5455
uses: codecov/codecov-action@v1
56+
with:
57+
flags: ${{ matrix.react }}
5558

5659
release:
5760
needs: main

package.json

+5-5
Original file line numberDiff line numberDiff line change
@@ -46,22 +46,22 @@
4646
"license": "MIT",
4747
"dependencies": {
4848
"@babel/runtime": "^7.12.5",
49-
"@testing-library/dom": "^8.0.0",
49+
"@testing-library/dom": "^8.5.0",
5050
"@types/react-dom": "*"
5151
},
5252
"devDependencies": {
5353
"@testing-library/jest-dom": "^5.11.6",
5454
"dotenv-cli": "^4.0.0",
5555
"kcd-scripts": "^11.1.0",
5656
"npm-run-all": "^4.1.5",
57-
"react": "^17.0.1",
58-
"react-dom": "^17.0.1",
57+
"react": "^18.0.0",
58+
"react-dom": "^18.0.0",
5959
"rimraf": "^3.0.2",
6060
"typescript": "^4.1.2"
6161
},
6262
"peerDependencies": {
63-
"react": "*",
64-
"react-dom": "*"
63+
"react": "^18.0.0",
64+
"react-dom": "^18.0.0"
6565
},
6666
"eslintConfig": {
6767
"extends": "./node_modules/kcd-scripts/eslint.js",

src/__tests__/act.js

+25-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import * as React from 'react'
2-
import {render, fireEvent, screen} from '../'
2+
import {act, render, fireEvent, screen} from '../'
33

44
test('render calls useEffect immediately', () => {
55
const effectCb = jest.fn()
@@ -43,3 +43,27 @@ test('calls to hydrate will run useEffects', () => {
4343
render(<MyUselessComponent />, {hydrate: true})
4444
expect(effectCb).toHaveBeenCalledTimes(1)
4545
})
46+
47+
test('cleans up IS_REACT_ACT_ENVIRONMENT if its callback throws', () => {
48+
global.IS_REACT_ACT_ENVIRONMENT = false
49+
50+
expect(() =>
51+
act(() => {
52+
throw new Error('threw')
53+
}),
54+
).toThrow('threw')
55+
56+
expect(global.IS_REACT_ACT_ENVIRONMENT).toEqual(false)
57+
})
58+
59+
test('cleans up IS_REACT_ACT_ENVIRONMENT if its async callback throws', async () => {
60+
global.IS_REACT_ACT_ENVIRONMENT = false
61+
62+
await expect(() =>
63+
act(async () => {
64+
throw new Error('thenable threw')
65+
}),
66+
).rejects.toThrow('thenable threw')
67+
68+
expect(global.IS_REACT_ACT_ENVIRONMENT).toEqual(false)
69+
})

src/__tests__/cleanup.js

+5-14
Original file line numberDiff line numberDiff line change
@@ -83,10 +83,7 @@ describe('fake timers and missing act warnings', () => {
8383
expect(microTaskSpy).toHaveBeenCalledTimes(0)
8484
// console.error is mocked
8585
// eslint-disable-next-line no-console
86-
expect(console.error).toHaveBeenCalledTimes(
87-
// ReactDOM.render is deprecated in React 18
88-
React.version.startsWith('18') ? 1 : 0,
89-
)
86+
expect(console.error).toHaveBeenCalledTimes(0)
9087
})
9188

9289
test('cleanup does not swallow missing act warnings', () => {
@@ -118,16 +115,10 @@ describe('fake timers and missing act warnings', () => {
118115
expect(deferredStateUpdateSpy).toHaveBeenCalledTimes(1)
119116
// console.error is mocked
120117
// eslint-disable-next-line no-console
121-
expect(console.error).toHaveBeenCalledTimes(
122-
// ReactDOM.render is deprecated in React 18
123-
React.version.startsWith('18') ? 2 : 1,
124-
)
118+
expect(console.error).toHaveBeenCalledTimes(1)
125119
// eslint-disable-next-line no-console
126-
expect(
127-
console.error.mock.calls[
128-
// ReactDOM.render is deprecated in React 18
129-
React.version.startsWith('18') ? 1 : 0
130-
][0],
131-
).toMatch('a test was not wrapped in act(...)')
120+
expect(console.error.mock.calls[0][0]).toMatch(
121+
'a test was not wrapped in act(...)',
122+
)
132123
})
133124
})

src/__tests__/new-act.js

+4-4
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
let asyncAct, consoleErrorMock
1+
let asyncAct
22

33
jest.mock('react-dom/test-utils', () => ({
44
act: cb => {
@@ -8,12 +8,12 @@ jest.mock('react-dom/test-utils', () => ({
88

99
beforeEach(() => {
1010
jest.resetModules()
11-
asyncAct = require('../act-compat').asyncAct
12-
consoleErrorMock = jest.spyOn(console, 'error').mockImplementation(() => {})
11+
asyncAct = require('../act-compat').default
12+
jest.spyOn(console, 'error').mockImplementation(() => {})
1313
})
1414

1515
afterEach(() => {
16-
consoleErrorMock.mockRestore()
16+
console.error.mockRestore()
1717
})
1818

1919
test('async act works when it does not exist (older versions of react)', async () => {

src/__tests__/no-act.js

-92
This file was deleted.

src/__tests__/old-act.js

-142
This file was deleted.

0 commit comments

Comments
 (0)