How do you use React context inside App.js file?

Viewed 1637

I am building a multi-language app. I am using react-intl. So far, so good. I made a state of the language with context api, so I can switch it easily. However I get this error when I try to use the state in App.js: TypeError: Object is not iterable (cannot read property Symbol(Symbol.iterator)).

Here is my context file:

import React, {useState, createContext} from 'react'


export const LanguageContext = createContext();

export const LanguageProvider = (props) => {
const [language, setLanguage] = useState('')

return (
    <LanguageContext.Provider value = {[language,setLanguage]}>
        {props.children}
    </LanguageContext.Provider>
)
}

And here is the App.js:

function App() {
const [language, setLanguage] = useContext(LanguageContext)
return (
<LanguageProvider>
  //i tried using locale={language} 
  <I18nProvider locale={LOCALES.language}>
    <CartProvider>
      <TableProvider>
        <div className="App">
          <Router>
            <Header />
            <Switch>
              <Route path='/Cart' component={Cart} />
              <Route path='/:group/:subGroup/:item' component={Item} />
              <Route path='/:group/:subGroup' component={Items} />
              <Route path='/' exact component={Home} />
              <Route path='/:group' component={Groups} />
            </Switch>
          </Router>
        </div>
      </TableProvider>
    </CartProvider>
  </I18nProvider>
</LanguageProvider>
);
}

export default App

Here is the locale file that Im using to pass to the I18nProvider :

export const LOCALES = {
ENGLISH : 'en',
FRENCH: 'fr'
}

And where I change the context value(another component, not App.js):

const [language, setLanguage] = useContext(LanguageContext)

following line is cut from jsx:

onClick={() => setLanguage('en')}

I thinks the problem might be because I am trying to access the context before the App.js return statement, where the provider wraps the children but even if this is the case, I still don't know what might fix it. Any help would be appreciated!

2 Answers

I thinks the problem might be because I am trying to access the context before the App.js return statement

You're right this is the problem.

Depending on where you want to use useContext you could create an extra component that is a child of LanguageProvider. Then inside this child you are able to use useContext.

To give a simplified example:

const App = () => {
  const [language, setLanguage] = useContext(LanguageContext);
  useEffect(() => {
    setLanguage('en');
  }, []);
  return <p>{language}</p>;
};

export default function AppWrapper() {
  return (
    <LanguageProvider>
      <App />
    </LanguageProvider>
  );
}

I had the same problem trying to apply an authentication flow with react-navigation v5. I tried to follow the documentation as it is: Authentication flows with react-navigation v5 But when trying to mix it with Context I run into the same issue

As in the previous Answer, I solve it in the same way. Here it's an example where it has 3 possible Screens Stacks:

I create a component where I'll be using the context

const RootStack = () => {
  const { state } = useContext(AuthContext);
  return (
      <Stack.Navigator>
          {false ? (
              <Stack.Screen name="Splash" component={SplashScreen} />
          ) : state.token === null ? (
              <Stack.Screen
                name="Authentication"
                component={AuthenticationStack}
                options={{
                  title: 'Sign in',
                  headerShown: false,
                  animationTypeForReplace: false ? 'pop' : 'push',
                }}
              />
          ) : (
              <Stack.Screen name="Home" component={AppStack} />
          )}
      </Stack.Navigator>
      );
  };

And then I insert the component inside the provider:

export default ({ navigation }) => {
  return (
    <AuthProvider>
      <NavigationContainer>
        <Stack.Navigator>
          <Stack.Screen
            name="RootStack"
            component={RootStack}
            options={{
            headerShown: false,
            }}
          />
        </Stack.Navigator>
      </NavigationContainer>
    </AuthProvider>
  );
};
Related