diff --git a/src/components/Provider.tsx b/src/components/Provider.tsx index 5f532c459..ba841f8cf 100644 --- a/src/components/Provider.tsx +++ b/src/components/Provider.tsx @@ -57,6 +57,21 @@ export interface ProviderProps< function Provider = UnknownAction, S = unknown>( providerProps: ProviderProps, ) { + if (process.env.NODE_ENV !== 'production') { + // The React Server Components build of React has no effect hooks + if (typeof useIsomorphicLayoutEffect !== 'function') { + throw new Error( + 'The React Redux `` component requires React hooks that are ' + + 'not available in a React Server Component. This usually means you are ' + + 'rendering `` in a Server Component. Add the `"use client"` ' + + 'directive to the top of the file that renders `` (or a ' + + 'parent that ends up rendering it) so it is treated as a Client ' + + 'Component. See https://react.dev/reference/rsc/use-client for more ' + + 'information.', + ) + } + } + const { children, context, serverState, store } = providerProps const contextValue = React.useMemo(() => { diff --git a/test/components/Provider-rsc.spec.tsx b/test/components/Provider-rsc.spec.tsx new file mode 100644 index 000000000..afd35c3a3 --- /dev/null +++ b/test/components/Provider-rsc.spec.tsx @@ -0,0 +1,31 @@ +import * as rtl from '@testing-library/react' +import { Provider } from 'react-redux' +import { createStore } from 'redux' + +// Simulates the React Server Components build of React, which has no effect hooks +vi.mock('../../src/utils/useIsomorphicLayoutEffect', () => ({ + useIsomorphicLayoutEffect: undefined, +})) + +// The bundled build inlines `useIsomorphicLayoutEffect`, so there is no module to mock +const describeSource = process.env.TEST_DIST ? describe.skip : describe + +describeSource('React', () => { + describe('Provider in a React Server Component environment', () => { + it('throws an actionable error when React effect hooks are unavailable', () => { + const store = createStore(() => ({})) + + const spy = vi.spyOn(console, 'error').mockImplementation(() => {}) + + expect(() => + rtl.render( + +
+ , + ), + ).toThrow(/React Server Component/) + + spy.mockRestore() + }) + }) +})