Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Throw an error if ApiProvider is nested inside a normal Provider. #3855

Merged
merged 2 commits into from
Nov 8, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 4 additions & 2 deletions errors.json
Original file line number Diff line number Diff line change
Expand Up @@ -31,5 +31,7 @@
"29": "`builder.addCase` cannot be called with an empty action type",
"30": "`builder.addCase` cannot be called with two reducers for the same action type",
"31": "\"middleware\" field must be a callback",
"32": "When using custom hooks for context, all hooks need to be provided: .\\nHook was either not provided or not a function."
}
"32": "When using custom hooks for context, all hooks need to be provided: .\\nHook was either not provided or not a function.",
"33": "Existing Redux context detected. If you already have a store set up, please use the traditional Redux setup.",
"34": "selectSlice returned undefined for an uninjected slice reducer"
}
12 changes: 10 additions & 2 deletions packages/toolkit/src/query/react/ApiProvider.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
import { configureStore } from '@reduxjs/toolkit'
import type { Context } from 'react'
import { useContext } from 'react'
import { useEffect } from 'react'
import React from 'react'
import type { ReactReduxContextValue } from 'react-redux'
import { Provider } from 'react-redux'
import { Provider, ReactReduxContext } from 'react-redux'
import { setupListeners } from '@reduxjs/toolkit/query'
import type { Api } from '@reduxjs/toolkit/query'

Expand Down Expand Up @@ -37,6 +38,13 @@ export function ApiProvider<A extends Api<any, {}, any, any>>(props: {
setupListeners?: Parameters<typeof setupListeners>[1] | false
context?: Context<ReactReduxContextValue>
}) {
const context = props.context || ReactReduxContext
const existingContext = useContext(context)
if (existingContext) {
throw new Error(
'Existing Redux context detected. If you already have a store set up, please use the traditional Redux setup.'
EskiMojo14 marked this conversation as resolved.
Show resolved Hide resolved
)
}
const [store] = React.useState(() =>
configureStore({
reducer: {
Expand All @@ -55,7 +63,7 @@ export function ApiProvider<A extends Api<any, {}, any, any>>(props: {
)

return (
<Provider store={store} context={props.context}>
<Provider store={store} context={context}>
{props.children}
</Provider>
)
Expand Down
13 changes: 13 additions & 0 deletions packages/toolkit/src/query/tests/apiProvider.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ import * as React from 'react'
import { createApi, ApiProvider } from '@reduxjs/toolkit/query/react'
import { fireEvent, render, waitFor } from '@testing-library/react'
import { waitMs } from './helpers'
import { Provider } from 'react-redux'
import { configureStore } from '@reduxjs/toolkit'

const api = createApi({
baseQuery: async (arg: any) => {
Expand Down Expand Up @@ -57,4 +59,15 @@ describe('ApiProvider', () => {
// Being that nothing has changed in the args, this should never fire.
expect(getByTestId('isFetching').textContent).toBe('false')
})
test('ApiProvider throws if nested inside a Redux context', () => {
expect(() =>
render(
<Provider store={configureStore({ reducer: () => null })}>
<ApiProvider api={api}>child</ApiProvider>
</Provider>
)
).toThrowErrorMatchingInlineSnapshot(
'"Existing Redux context detected. If you already have a store set up, please use the traditional Redux setup."'
)
})
})