3

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.

Sanketh B. K
  • 759
  • 8
  • 22
  • 1
    These are all completely separate and independent components, I don't think it matters really. Are you seeing an issue with a specific implementation? – Drew Reese Jun 17 '21 at 07:20
  • Yes when i wrapped it completely outside, redux store had issue and wrapping inside Router made the router non-functional. I will post the exact error messages at each level of wrapping. – Sanketh B. K Jun 17 '21 at 07:23
  • 1
    Try a bit of debugging: try each one individually to see which work alone, then try combos of two. I suspect you've not setup your Redux provider correctly since you *actually* see an error there. Can you share all providers, and what router you are importing. https://stackoverflow.com/help/minimal-reproducible-example The error seems to say that *something* is looking for a `store` in your `AppProvider` component. – Drew Reese Jun 17 '21 at 07:38
  • Redux store is working fine. It was expecting the store prop in AppProvider component but actually provider is supposed to be the outer component which gets the store prop. So it is throwing error. – Sanketh B. K Jun 17 '21 at 07:49
  • 1
    Perhaps I'm missing something here, your `AppProvider` isn't doing anything with any `store` prop, and shouldn't have any bearing on anything from, or to, your redux provider. Can you create a *running* codesandbox that reproduces this issue that we can inspect and live debug in? – Drew Reese Jun 17 '21 at 07:52
  • Hey sorry for late reply. I tried to replicate it but it it has lot of states and dependencies and thank you for help. i believe the error is becuase i am using redux store in context.js. but i don't understand why the router stops functioning if i place the AppProvider component inside and . i added my context.js in edit 2 please check it out. – Sanketh B. K Jun 17 '21 at 09:10
  • What if you completely remove `AppContext` and `AppProvider` (fix any broken references), does your app render without issue then? The router works? It's a bit odd to have a Redux provider ***and*** your own context provider that is only providing *some* of the redux state. – Drew Reese Jun 17 '21 at 15:15
  • Yes removing AppProvide will remove the error and everything works like before, even router works as expected. I wanted to write a useContext becuase there are some states in redux store which aare used by every components. So i accessed put those state variables in a useContext hook. I can still work without useContext hook by accessing them each time from redux store in every time. I just thought it using useContext might be a cleaner way, not sure if it is a valid way. – Sanketh B. K Jun 17 '21 at 15:21
  • I see. 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` and `const isloggedIn = useSelector(state => state.userLogin.isLoggedIn);`? Simpler and less moving parts than adding an entire other Context Provider to your app. – Drew Reese Jun 17 '21 at 15:48

1 Answers1

1

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();
Drew Reese
  • 165,259
  • 14
  • 153
  • 181