At what nesting the Context Provider should be wrapped while using react-router and redux?

Viewed 2589

When i was learning react I learnt to create a context.js file and put all the states which are used throughout the component tree and wrap the root component in index.js with context provider so that entire app can access them.

Like this

context.js

import React, { useState, useContex} from 'react';

const AppContext = React.createContext();

const AppProvider = (props) => {
  return (
    <AppContext.Provider value={{ isLoggedIn: props.isLoggedIn }}>
      {props.chidren}
    </AppContext.Provider>
  );
} 

export const useGlobalContext = () => {
  return useContext(AppContext);
};

index.js


ReactDOM.render(
  <React.StrictMode>
    <AppProvider>
      <App />
    </AppProvider>
  </React.StrictMode>,
  document.getElementById('root')
);

And i used useGlobalContext custom hook to access those variables.

But now i am using react-router and redux in my project. But i still have the necessity to use global context for some state variables. The problem is i am not able to figure out at what nesting level should i wrap the <AppContext.Provider> in my index.js without disturbing the functionality of react-router and redux.

current index.js

nst Routing = () => {
  console.log('store = ', store);
  return (
    <Provider store={store}>
      <PersistGate persistor={persistor} loading={Loading}>
        <Router>
          <React.StrictMode>
            <Header />
            <Switch>
              <Route exact path='/' component={App} />
              <Route path='/register' component={Register} />
              <Route path='/login' component={Login} />
              <Route path='/logout' component={Logout} />
              <Route path='/verify-email' component={VerifyEmail} />
            </Switch>
            <Footer />
          </React.StrictMode>
        </Router>
      </PersistGate>
    </Provider>
  );
};

ReactDOM.render(<Routing />, document.getElementById('root'));

Thanks in advance.

Edit: 1

Error messages at each wrapping level

  1. outermost

    const Routing = () => {
      console.log('store = ', store);
      return (
        <AppProvider>
          <Provider store={store}>
            <PersistGate persistor={persistor} loading={Loading}>
              <Router>
  
              </Router>
            </PersistGate>
          </Provider>
        </AppProvider>
      );
    };

Error message Error: Could not find "store" in the context of "Connect(AppProvider)". Either wrap the root component in a , or pass a custom React context provider to and the corresponding React context consumer to Connect(AppProvider) in connect options.

  1. 1st level nesting
const Routing = () => {
  console.log('store = ', store);
  return (
    <Provider store={store}>
      <AppProvider>
        <PersistGate persistor={persistor} loading={Loading}>
          <Router>
            <React.StrictMode>
              <Header />
              <Switch>
                <Route exact path='/' component={App} />
                <Route path='/register' component={Register} />
                <Route path='/login' component={Login} />
                <Route path='/logout' component={Logout} />
                <Route path='/verify-email' component={VerifyEmail} />
              </Switch>
              <Footer />
            </React.StrictMode>
          </Router>
        </PersistGate>
      </AppProvider>
    </Provider>
  );
};

Error: No error but react router not working.

  1. Inside router
const Routing = () => {
  console.log('store = ', store);
  return (
    <Provider store={store}>
      <PersistGate persistor={persistor} loading={Loading}>
        <Router>
          <AppProvider>
            <React.StrictMode>
              <Header />
              <Switch>
                <Route exact path='/' component={App} />
                <Route path='/register' component={Register} />
                <Route path='/login' component={Login} />
                <Route path='/logout' component={Logout} />
                <Route path='/verify-email' component={VerifyEmail} />
              </Switch>
              <Footer />
            </React.StrictMode>
          </AppProvider>
        </Router>
      </PersistGate>
    </Provider>
  );
};

Error: Same router not working,

Edit 2

This is my context.js file

import React, { useContext } from 'react';
import { connect } from 'react-redux';

// This file contains all state store variables that will be useful throughout the application.
// Thuis component wraps the whole component tree and provides a context to use these varaibles

const AppContext = React.createContext();

const AppProvider = (props) => {
  return (
    <AppContext.Provider
    // value={{ isLoggedIn: props.isLoggedIn }}
    >
      {props.chidren}
    </AppContext.Provider>
  );
};

const mapStateToProps = (state) => {
  return {
    isloggedIn: state.userLogin.isLoggedIn,
  };
};

export const useGlobalContext = () => {
  return useContext(AppContext);
};

export default connect(mapStateToProps, null)(AppProvider);

Since i am using redux store inside context.js it has to be inside and . But if i place it inside them Router will not work.

1 Answers

I don't see any reason for your AppProvider to not work other than maybe an error is thrown accessing into the state object and your app/redux connection falls over. Are you sure state.userLogin.isLoggedIn is valid?

Why not use the useSelector provided by react-redux

const isloggedIn = useSelector(state => state.userLogin.isLoggedIn);

It is simpler and has less moving parts than adding an entire other Context Provider to your app.

If this is something you do in almost every component you can create a custom hook for the slices of state you pull in every component.

import { useSelector } from 'react-redux';

const useCustomSelector = () {
  const isloggedIn = useSelector(state => state.userLogin.isLoggedIn);
  // pull other state

  return {
    isloggedIn,
    // return other state
  };
};

Usage:

const { isloggedIn } = useCustomSelector();
Related