98

I am trygin to use React-Redux library and I am getting the error on the title. I wrapped my components with Provider but I still get the error, only if I implement the useDispatch() hook.

The app worked fine, until I added the useDispatch() line. The rest of lines regarding the dispatch function can be removed and I still get the same error.

If you could help me I would really appreciate it. Thanks

Here is my code:

import 'react-native-gesture-handler';
import {NavigationContainer} from '@react-navigation/native';
import Navigator from './navigation/Navigator';

import React, {useEffect, useState, useCallback} from 'react';
import {SafeAreaView, StyleSheet, Text, View} from 'react-native';

import {createStore, combineReducers} from 'redux';
import {Provider, useDispatch} from 'react-redux';
import dataReducer from './store/reducers/dataReducer';
import {CONSTANTS} from './constants/constants';
import {saveInitialData} from './store/actions/dataActions';

const App = () => {
  const [fetched, setFetched] = useState(initialState);

  const dispatch = useDispatch();

  const saveInitialDataHandler = useCallback(data => {
    dispatch(saveInitialData(data));
    callback;
  }, []);

  const rootReducer = combineReducers({
    content: dataReducer,
  });

  const store = createStore(rootReducer);

  useEffect(() => {
    fetchData();
  }, []);

  const fetchData = () => {
    fetch(CONSTANTS.database)
      .then(response => response.json())
      .then(responseJSON => {
        setFetched(true);
        saveInitialDataHandler(responseJSON);
      });
  };

  if (!fetched) {
    return (
      <Provider store={store}>
        <View stlye={{flex: 1, alignItems: 'center', justifyContent: 'center'}}>
          <Text></Text>
        </View>
      </Provider>
    );
  } else {
    return (
      <Provider store={store}>
        <NavigationContainer>
          <SafeAreaView style={styles.SafeAreaView}>
            <Navigator></Navigator>
          </SafeAreaView>
        </NavigationContainer>
      </Provider>
    );
  }
};

const styles = StyleSheet.create({
  SafeAreaView: {flex: 1},
});

export default App;
MIPB
  • 1,770
  • 2
  • 12
  • 21

8 Answers8

124

App must be wrapped in provider since you are using useDispatch in it. Right now it's just a child. Provider sets the context so only its children can have access to it, not a parent.

One solution would be to create a wrapper component for it:

const AppWrapper = () => {
  const store = createStore(rootReducer);

  return (
    <Provider store={store}> // Set context
      <App /> // Now App has access to context
    </Provider>
  )
}

const App = () => {
  const dispatch = useDispatch(); // Works!
...
Brian Thompson
  • 13,263
  • 4
  • 23
  • 43
  • 4
    Thank you, it worked. For anyone using it, also remember to export your AppWrapper, instead of your App! – MIPB Feb 21 '20 at 13:14
  • Basically make sure to wrap your App component with the Provider not index.js file which mounts the App component to the DOM. – Safwat Fathi Apr 18 '21 at 13:10
  • Hello, I am having this same issue and when I try this method I receive "ReferenceError: Can't find variable: rootReducer" – Carlos Craig May 02 '21 at 04:10
  • 1
    `rootReducer` is the name the OP gave to their combined reducers, yours may be different depending on how you've set up your reducers and imported them. – Brian Thompson May 02 '21 at 13:12
27

If you get this error when run npm run test Then issues is related your test file.

Update or replace your app.test.tsx by below code

NB: Don't forget to install redux-mock-store if you don't have already.

import React from 'react';
import { render } from '@testing-library/react';

import App from './App';

import { Provider } from 'react-redux';
import configureStore from 'redux-mock-store';

describe('With React Testing Library', () => {
    const initialState = { output: 10 };
    const mockStore = configureStore();
    let store;

    it('Shows "Hello world!"', () => {
        store = mockStore(initialState);
        const { getByText } = render(
            <Provider store={store}>
                <App />
            </Provider>
        );

        expect(getByText('Hello World!')).not.toBeNull();
    });
});

I got this solution after searching 1 hours. Thanks a lot to OSTE

Original Solution: Github issues/8145 and solution link


With this solution if you get error like TypeError: window.matchMedia is not a function then solve by this way. add those line to your setupTests.ts file. Original solution link stackoverflow.com/a/64872224/5404861

global.matchMedia = global.matchMedia || function () {
  return {
    addListener: jest.fn(),
    removeListener: jest.fn(),
  };
};
MD Ashik
  • 9,117
  • 10
  • 52
  • 59
24

maybe you imported Provider into the App.js file. Provider needs to be imported into the file index.js.

/*index.js*/
import React from 'react';
import ReactDOM from 'react-dom';
import { Provider } from 'react-redux';
import { store } from './store/reduxStore';
import './index.css';
import App from './App';
import reportWebVitals from './reportWebVitals';

ReactDOM.render(
  <React.StrictMode>
    <Provider store={store}>
      <App />
    </Provider>
  </React.StrictMode>,
  document.getElementById('root')
);
Paweł Gościcki
  • 9,066
  • 5
  • 70
  • 81
Matteo Zappalà
  • 241
  • 2
  • 3
5

I did this on the same index.js file for a react-native app. This way you avoid having to export and add another file just for giving wrapping the App with the provider.

const ReduxProvider = () => {
    return(
        <Provider store={store}>
            <App />
        </Provider>
    )
}

AppRegistry.registerComponent(appName, () => ReduxProvider);
Dharman
  • 30,962
  • 25
  • 85
  • 135
Pixele9
  • 422
  • 8
  • 25
2

You can not call

const dispatch = useDispatch()

in a component that is not inside the container

<Provider store={store}></Provider>

Correct example:

const App = ({ Component, pageProps }: AppPropsWithLayout) => {
  const page = (
    <>
      <Component {...pageProps} />
      <Analytics />
    </>
  )

  if (Component.getLayout) {
    return <Provider store={store}>Component.getLayout(page)</Provider>
  }

  return (
    <Provider store={store}>
      <MainLayout>{page}</MainLayout>
    </Provider>
  )
}
1
import store from "../redux/Store";

store?.dispatch(actions.setSocket({ socket }));

first save the action. Then use dispatch with that store if the component is not saved in Provider.

Sehrish Waheed
  • 1,230
  • 14
  • 17
0

Happened to me, when I was calling React component as a function, without using it as virtual dom, Comp was independently called not rendered as child of some element

function Comp() {
  const a = useSelector(selectA); // throws error
}
Comp();

so in my case solution was to call Comp and a component, not as a function i.e <Comp />

Akshay Vijay Jain
  • 13,461
  • 8
  • 60
  • 73
0

Sometimes not specifying a client-rendered component in Next.js, throws this error for redux...

So, add 'use client' before importing any libraries, in my case, this fixed my issue