Skip to content

Commit

Permalink
Migrate to React 19 (#4409)
Browse files Browse the repository at this point in the history
* Update `react-redux` to 9.1.2

* Run tests against different versions of React during CI

* Remove unnecessary `tick` and `runAllTimers` from `cleanup.test.tsx`

* Remove redundant `act` wrappers around `fireEvent` method calls

- According to latest documentation of `@testing-library/react`, `fireEvent` methods are already wrapped inside `act` calls making manual wrapping unnecessary.

* Workaround issue `userEvent` not working with fake timers

- There currently seems to be an issue involving `sinon` fake timers used by `vitest`, `@testing-library/react` only supporting `jest` fake timers and  `@testing-library/user-event` using `setTimeout` internally to simulate user actions such as button presses. Currently `@testing-library/react` only works with `jest` fake timers, which means if there are any component updates while `sinon` fake timers are running in `vitest` , `@testing-library/react` will not catch it and things start to break.
- To workaround this issue, We have to setup the `user` by calling `userEvent.setup({ delay: null })`. The reason why We do this is because `@testing-library/user-event` uses `setTimeout` internally which cannot be awaited in a test while fake timers are running as it can cause the tests to indefinitely hang. So the current workaround is to disable the `delay` functionality of `userEvent` and prevent it from calling `setTimeout`. We also have to pass in `shouldAdvanceTime: true` to `vi.useFakeTimers()` as it can get around the issue of `@testing-library/react` not tracking `sinon` fake timers in `vitest`.

* Fix test names in `fork.test.ts`

- Fixed test names in `fork.test.ts` which could cause the terminal to flicker in Windows.

* Fix issue with `console` spy inside `buildHooks.test.tsx`

- `.mockReset()` should not be called in spies since it calls `.mockClear()` and returns the implementation to its **initial** form. In this case it was silencing some of the `act` related warnings emitted by `@testing-library/react` which needed to be resolved since they were calling issues. So `.mockReset()` calls on spies need to be changed to `.mockRestore()` calls since `.mockRestore()` restores the implementation to its **original** form.

* Fix `act` related issues in `buildHooks.test.tsx`

* Fix issues related to spies and stubbing environments in `utils.spec.ts`

* Fix `console` spy related issues in `effectScenarios.test.ts`

* Fix `console` spy related issues in `listenerMiddleware.test.ts`

* Fix `console` spy related issues in `createApi.test.ts`

* Fix wrong `test.each` and `describe.each` calls

* Fix `console` spy related issues in `devWarnings.test.tsx`

* Fix `console` spy related issues in `injectEndpoints.test.tsx`

* Fix issue with test names in `queryFn.test.tsx`

* Migrate docs to React 19

* Update `peerDependencies` of toolkit to include React v19

* Update React and React-DOM to the new rc version

* Fix issues related to `console` spies in `createAsyncThunk.test.ts`

* Fix issues related to stubbing envs in `createAsyncThunk.test.ts`

* Fix issues related to `console` spies in `createReducer.test.ts`

* Fix issues related to stubbing envs in `createReducer.test.ts`

* Fix issues related to `console` spies in `createSlice.test.ts`

* Fix issues related to stubbing envs in `createSlice.test.ts`

* Fix issues related to `console` spies in `immutableStateInvariantMiddleware.test.ts`

* Fix issues related to `console` spies in `serializableStateInvariantMiddleware.test.ts`

* Change `.toHaveBeenCalledTimes(1)` to `.toHaveBeenCalledOnce()`

* Change `.toHaveBeenCalledTimes(0)` to `.not.toHaveBeenCalled()`

* Set `@types/react` and `@types/react-dom` to temporary types packages

- We set the `@types/react` and `@types/react-dom` package resolutions to `npm:types-react` and `npm:types-react-dom` according to the React 19 migration guide.

* Add `areErrorsEqual` equality tester

- This was done to make sure `toHaveBeenCalledWith` will fail if we pass in the wrong Error constructor. For example the assertion will now fail if we pass in an `Error` instead of a `TypeError`.

* Fix `console` spy related issues in `fakeBaseQuery.test.tsx`

* Fix `console` spy related issues in `queryFn.test.tsx`

* Remove `satisfies` operators in `queryFn.test.tsx`

* Remove `jest-snapshot` from `resolutions` field

- This was done because it was causing the unit tests in example workspaces to fail.

* Remove `console-testing-library` as it is no longer needed

* Fix minor JSX related type issues

* Bump `jsdom` to version 25.0.1

* Bump `@testing-library/react` to version 16.1.0

* Properly `await` assertion in `tests/fork.test.ts`

* Properly `await` assertion in `src/query/tests/errorHandling.test.tsx`

* Migrate to React 19

* Bump TS version in website

---------

Co-authored-by: Mark Erikson <[email protected]>
  • Loading branch information
aryaemami59 and markerikson authored Dec 11, 2024
1 parent e848a55 commit 40b8aed
Show file tree
Hide file tree
Showing 59 changed files with 10,807 additions and 8,025 deletions.
50 changes: 44 additions & 6 deletions .github/workflows/tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -62,13 +62,27 @@ jobs:
path: packages/toolkit/package.tgz

test-dist:
name: Test against dist
name: Run local tests against build artifact (React ${{ matrix.react.version }})
needs: [build]
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
node: ['20.x']
react:
[
{
version: '^18',
types: ^18,
react-dom: { version: '^18', types: '^18' },
},
{
version: '^19',
types: '^19',
react-dom: { version: '^19', types: '^19' },
},
]

steps:
- name: Checkout repo
uses: actions/checkout@v4
Expand All @@ -82,17 +96,23 @@ jobs:
- name: Install deps
run: yarn install

- uses: actions/download-artifact@v4
- name: Download build artifact
uses: actions/download-artifact@v4
with:
name: package
path: packages/toolkit

- run: ls -lah
- name: Check folder contents
run: ls -lah

- name: Install React ${{ matrix.react.version }} and React-DOM ${{ matrix.react.react-dom.version }}
run: yarn up react@${{ matrix.react.version }} react-dom@${{ matrix.react.react-dom.version }} @types/react@${{ matrix.react.types }} @types/react-dom@${{ matrix.react.react-dom.types }}

- name: Install build artifact
run: yarn workspace @reduxjs/toolkit add $(pwd)/package.tgz

- run: sed -i -e /@remap-prod-remove-line/d ./tsconfig.base.json
- name: Erase path aliases
run: sed -i -e /@remap-prod-remove-line/d ./tsconfig.base.json

- name: Run tests, against dist
env:
Expand All @@ -103,7 +123,7 @@ jobs:
run: rm -rf dist && yarn tsc -p . --moduleResolution Bundler --module ESNext --noEmit false --declaration --emitDeclarationOnly --outDir dist --target ESNext && rm -rf dist

test-types:
name: 'Test Types: TS ${{ matrix.ts }}'
name: 'Test Types: TS ${{ matrix.ts }} and React ${{ matrix.react.version }}'

needs: [build]
runs-on: ubuntu-latest
Expand All @@ -112,6 +132,20 @@ jobs:
matrix:
node: ['20.x']
ts: ['5.0', '5.1', '5.2', '5.3', '5.4', '5.5']
react:
[
{
version: '^18',
types: ^18,
react-dom: { version: '^18', types: '^18' },
},
{
version: '^19',
types: '^19',
react-dom: { version: '^19', types: '^19' },
},
]

steps:
- name: Checkout repo
uses: actions/checkout@v4
Expand All @@ -125,6 +159,9 @@ jobs:
- name: Install deps
run: yarn install

- name: Install React ${{ matrix.react.version }} and React-DOM ${{ matrix.react.react-dom.version }}
run: yarn up react@${{ matrix.react.version }} react-dom@${{ matrix.react.react-dom.version }} @types/react@${{ matrix.react.types }} @types/react-dom@${{ matrix.react.react-dom.types }}

- name: Install TypeScript ${{ matrix.ts }}
run: yarn add typescript@${{ matrix.ts }}

Expand All @@ -139,7 +176,8 @@ jobs:
- name: Show installed RTK versions
run: yarn info @reduxjs/toolkit

- run: sed -i -e /@remap-prod-remove-line/d ./tsconfig.base.json
- name: Erase path aliases
run: sed -i -e /@remap-prod-remove-line/d ./tsconfig.base.json

- name: Test types
env:
Expand Down
68 changes: 0 additions & 68 deletions .yarn/patches/console-testing-library-npm-0.6.1-4d9957d402.patch

This file was deleted.

26 changes: 0 additions & 26 deletions .yarn/patches/console-testing-library__npm_0.3.1.patch

This file was deleted.

2 changes: 1 addition & 1 deletion docs/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
"devDependencies": {
"@manaflair/redux-batch": "^1.0.0",
"@types/nanoid": "^2.1.0",
"@types/react": "^18.0",
"@types/react": "^19.0.1",
"async-mutex": "^0.3.2",
"axios": "^0.20.0",
"formik": "^2.1.5",
Expand Down
50 changes: 34 additions & 16 deletions docs/tutorials/quick-start.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -84,21 +84,30 @@ export const store = configureStore({

// file: index.tsx
import React from 'react'
import ReactDOM from 'react-dom'
import { createRoot } from 'react-dom/client'
import './index.css'
import App from './App'
// highlight-start
import { store } from './app/store'
import { Provider } from 'react-redux'
// highlight-end

ReactDOM.render(
// highlight-next-line
<Provider store={store}>
<App />
</Provider>,
document.getElementById('root')
)
const container = document.getElementById('root')

if (container) {
const root = createRoot(container)

root.render(
// highlight-next-line
<Provider store={store}>
<App />
</Provider>,
)
} else {
throw new Error(
"Root element with ID 'root' was not found in the document. Ensure there is a corresponding HTML element with the ID 'root' in your HTML file.",
)
}
```

### Create a Redux State Slice
Expand Down Expand Up @@ -214,18 +223,27 @@ export type RootState = ReturnType<typeof store.getState>

// file: index.tsx noEmit
import React from 'react'
import ReactDOM from 'react-dom'
import { createRoot } from 'react-dom/client'
import { Counter } from './features/counter/Counter'
import { store } from './app/store'
import { Provider } from 'react-redux'

ReactDOM.render(
// highlight-next-line
<Provider store={store}>
<Counter />
</Provider>,
document.getElementById('root')
)
const container = document.getElementById('root')

if (container) {
const root = createRoot(container)

root.render(
// highlight-next-line
<Provider store={store}>
<Counter />
</Provider>,
)
} else {
throw new Error(
"Root element with ID 'root' was not found in the document. Ensure there is a corresponding HTML element with the ID 'root' in your HTML file.",
)
}

// file: features/counter/Counter.tsx
import React from 'react'
Expand Down
24 changes: 16 additions & 8 deletions docs/tutorials/rtk-query.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -150,19 +150,27 @@ export const store = configureStore({

// file: index.tsx
import * as React from 'react'
import { render } from 'react-dom'
import { createRoot } from 'react-dom/client'
import { Provider } from 'react-redux'

import App from './App'
import { store } from './store'

const rootElement = document.getElementById('root')
render(
<Provider store={store}>
<App />
</Provider>,
rootElement
)
const container = document.getElementById('root')

if (container) {
const root = createRoot(container)

root.render(
<Provider store={store}>
<App />
</Provider>,
)
} else {
throw new Error(
"Root element with ID 'root' was not found in the document. Ensure there is a corresponding HTML element with the ID 'root' in your HTML file.",
)
}
```

## Use the query in a component
Expand Down
2 changes: 1 addition & 1 deletion docs/usage/nextjs.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -181,7 +181,7 @@ export default function StoreProvider({
children: React.ReactNode
}) {
// highlight-start
const storeRef = useRef<AppStore>()
const storeRef = useRef<AppStore>(undefined)
if (!storeRef.current) {
// Create the store instance the first time this renders
storeRef.current = makeStore()
Expand Down
26 changes: 18 additions & 8 deletions docs/usage/usage-guide.md
Original file line number Diff line number Diff line change
Expand Up @@ -1041,6 +1041,7 @@ configureStore({
If using Redux-Persist, you should specifically ignore all the action types it dispatches:

```jsx
import { createRoot } from 'react-dom/client'
import { configureStore } from '@reduxjs/toolkit'
import {
persistStore,
Expand Down Expand Up @@ -1078,14 +1079,23 @@ const store = configureStore({

let persistor = persistStore(store)

ReactDOM.render(
<Provider store={store}>
<PersistGate loading={null} persistor={persistor}>
<App />
</PersistGate>
</Provider>,
document.getElementById('root'),
)
const container = document.getElementById('root')

if (container) {
const root = createRoot(container)

root.render(
<Provider store={store}>
<PersistGate loading={null} persistor={persistor}>
<App />
</PersistGate>
</Provider>,
)
} else {
throw new Error(
"Root element with ID 'root' was not found in the document. Ensure there is a corresponding HTML element with the ID 'root' in your HTML file.",
)
}
```

Additionally, you can purge any persisted state by adding an extra reducer to the specific slice that you would like to clear when calling persistor.purge(). This is especially helpful when you are looking to clear persisted state on a dispatched logout action.
Expand Down
10 changes: 5 additions & 5 deletions examples/action-listener/counter/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,12 @@
"dependencies": {
"@reduxjs/toolkit": "^1.8.0",
"@types/node": "^12.0.0",
"@types/react": "^18.0.12",
"@types/react-dom": "^18.0.5",
"@types/react": "^19.0.1",
"@types/react-dom": "^19.0.1",
"clsx": "1.1.1",
"react": "^18.1.0",
"react-dom": "^18.1.0",
"react-redux": "^9.1.0",
"react": "^19.0.0",
"react-dom": "^19.0.0",
"react-redux": "^9.1.2",
"react-scripts": "5.0.1",
"typescript": "~4.9"
},
Expand Down
Loading

0 comments on commit 40b8aed

Please sign in to comment.