Skip to content

Commit

Permalink
feat: Add support for React 18 (#1031)
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
eps1lon and ph-fritsche committed Mar 31, 2022
1 parent 0c4aabe commit ccd8a0d
Show file tree
Hide file tree
Showing 14 changed files with 370 additions and 444 deletions.
3 changes: 3 additions & 0 deletions .github/workflows/validate.yml
Expand Up @@ -16,6 +16,7 @@ jobs:
# ignore all-contributors PRs
if: ${{ !contains(github.head_ref, 'all-contributors') }}
strategy:
fail-fast: false
matrix:
# 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
node: [12, 14, '16.9.1']
Expand Down Expand Up @@ -52,6 +53,8 @@ jobs:

- name: ⬆️ Upload coverage report
uses: codecov/codecov-action@v1
with:
flags: ${{ matrix.react }}

release:
needs: main
Expand Down
10 changes: 5 additions & 5 deletions package.json
Expand Up @@ -46,22 +46,22 @@
"license": "MIT",
"dependencies": {
"@babel/runtime": "^7.12.5",
"@testing-library/dom": "^8.0.0",
"@testing-library/dom": "^8.5.0",
"@types/react-dom": "*"
},
"devDependencies": {
"@testing-library/jest-dom": "^5.11.6",
"dotenv-cli": "^4.0.0",
"kcd-scripts": "^11.1.0",
"npm-run-all": "^4.1.5",
"react": "^17.0.1",
"react-dom": "^17.0.1",
"react": "^18.0.0",
"react-dom": "^18.0.0",
"rimraf": "^3.0.2",
"typescript": "^4.1.2"
},
"peerDependencies": {
"react": "*",
"react-dom": "*"
"react": "^18.0.0",
"react-dom": "^18.0.0"
},
"eslintConfig": {
"extends": "./node_modules/kcd-scripts/eslint.js",
Expand Down
26 changes: 25 additions & 1 deletion src/__tests__/act.js
@@ -1,5 +1,5 @@
import * as React from 'react'
import {render, fireEvent, screen} from '../'
import {act, render, fireEvent, screen} from '../'

test('render calls useEffect immediately', () => {
const effectCb = jest.fn()
Expand Down Expand Up @@ -43,3 +43,27 @@ test('calls to hydrate will run useEffects', () => {
render(<MyUselessComponent />, {hydrate: true})
expect(effectCb).toHaveBeenCalledTimes(1)
})

test('cleans up IS_REACT_ACT_ENVIRONMENT if its callback throws', () => {
global.IS_REACT_ACT_ENVIRONMENT = false

expect(() =>
act(() => {
throw new Error('threw')
}),
).toThrow('threw')

expect(global.IS_REACT_ACT_ENVIRONMENT).toEqual(false)
})

test('cleans up IS_REACT_ACT_ENVIRONMENT if its async callback throws', async () => {
global.IS_REACT_ACT_ENVIRONMENT = false

await expect(() =>
act(async () => {
throw new Error('thenable threw')
}),
).rejects.toThrow('thenable threw')

expect(global.IS_REACT_ACT_ENVIRONMENT).toEqual(false)
})
19 changes: 5 additions & 14 deletions src/__tests__/cleanup.js
Expand Up @@ -83,10 +83,7 @@ describe('fake timers and missing act warnings', () => {
expect(microTaskSpy).toHaveBeenCalledTimes(0)
// console.error is mocked
// eslint-disable-next-line no-console
expect(console.error).toHaveBeenCalledTimes(
// ReactDOM.render is deprecated in React 18
React.version.startsWith('18') ? 1 : 0,
)
expect(console.error).toHaveBeenCalledTimes(0)
})

test('cleanup does not swallow missing act warnings', () => {
Expand Down Expand Up @@ -118,16 +115,10 @@ describe('fake timers and missing act warnings', () => {
expect(deferredStateUpdateSpy).toHaveBeenCalledTimes(1)
// console.error is mocked
// eslint-disable-next-line no-console
expect(console.error).toHaveBeenCalledTimes(
// ReactDOM.render is deprecated in React 18
React.version.startsWith('18') ? 2 : 1,
)
expect(console.error).toHaveBeenCalledTimes(1)
// eslint-disable-next-line no-console
expect(
console.error.mock.calls[
// ReactDOM.render is deprecated in React 18
React.version.startsWith('18') ? 1 : 0
][0],
).toMatch('a test was not wrapped in act(...)')
expect(console.error.mock.calls[0][0]).toMatch(
'a test was not wrapped in act(...)',
)
})
})
8 changes: 4 additions & 4 deletions src/__tests__/new-act.js
@@ -1,4 +1,4 @@
let asyncAct, consoleErrorMock
let asyncAct

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

beforeEach(() => {
jest.resetModules()
asyncAct = require('../act-compat').asyncAct
consoleErrorMock = jest.spyOn(console, 'error').mockImplementation(() => {})
asyncAct = require('../act-compat').default
jest.spyOn(console, 'error').mockImplementation(() => {})
})

afterEach(() => {
consoleErrorMock.mockRestore()
console.error.mockRestore()
})

test('async act works when it does not exist (older versions of react)', async () => {
Expand Down
92 changes: 0 additions & 92 deletions src/__tests__/no-act.js

This file was deleted.

142 changes: 0 additions & 142 deletions src/__tests__/old-act.js

This file was deleted.

0 comments on commit ccd8a0d

Please sign in to comment.