kingofeverything
kingofeverything

Reputation: 41

How to use redux component in App.js React Native?

I'm doing a simple counter app. It has one label, and a button that you can increment by + 1 (each time it's pushed).

Using redux, I want to use the count that I store (in my Redux Store) in App.js file. However, I'm getting an error:

Error: could not find react-redux context value; please ensure the component is wrapped in a Provider

Using the useSelector works in other files, just not App.js. Is there a work around?

import React from 'react';
import { StyleSheet, Text, View } from 'react-native';
import Dogs from './components/Dogs';
import { Provider, useSelector } from 'react-redux';
import store from './redux/configureStore'

export default function App() {
  const count = useSelector((state) => state.counter.count);
{/*useSelector does not work in this file!*/}
  return (
    
    <Provider store={store}>
      <View style={styles.container}>
        <Text>{`ha ${count}`}</Text>
        <Dogs />
      </View>
    </Provider>
  );
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    backgroundColor: '#fff',
    alignItems: 'center',
    justifyContent: 'center',
  },
});

Counter.js

import React, { useState, useEffect } from "react";
import { View, Text, StyleSheet, Button} from "react-native";
import { useDispatch, useSelector } from "react-redux";
import { increment } from '../redux/ducks/counter'

const Counter = () => {
    const count = useSelector((state) => state.counter.count);
 {/*useSelector works in this file!*/}
    const dispatch = useDispatch();

    const handleIncrement = () => {
        dispatch(increment())
    };

    return (
      <div>
        {/* <Text>{` COunt: ${count}`}</Text> */}
        <Button onPress={handleIncrement}>Increment</Button>
      </div>
    );
}

const styles = StyleSheet.create({})

export default Counter;

redux/configureStore.js

import { combineReducers, createStore } from 'redux';
import counterReducer from './ducks/counter';

const reducer = combineReducers({
    counter: counterReducer
});

const store = createStore(reducer);

export default store;

redux/ducks/counter.js

const INCREMENT = 'increment';

export const increment = () => ({
    type: INCREMENT
})

const initialState = {
    count: 0
};

export default ( state = initialState, action) => {
    switch(action.type) {
        case INCREMENT:
            return{...state, count: state.count + 1}
        default:
            return state;
    }
};

Upvotes: 0

Views: 2113

Answers (3)

Rahul Prabakar
Rahul Prabakar

Reputation: 1

Unlike a regular React application, an expo React-Native application is not wrapped using an index.js file. Therefore when we wrap the provider in app.js for a React-Native app, we wrap it in index.js for React application. So the hooks like useSelector or useDispatch run before the provider is initialized. So, I would suggest not using any hooks in the app component, instead, we can create other components in the app.js and use the hooks in a separate component like in the code I have used below.

const Root = () => {

const [appIsReady, setAppIsReady] = useState(false);
const dispatch = useDispatch();

const fetchToken = async () => {
const token = await AsyncStorage.getItem("token");
console.log("Stored Token:  ", token);
if (token) {
  dispatch(setAuthLogin({ isAuthenticated: true, token }));
 }
};

const LoadFonts = async () => {
 await useFonts();
};

useEffect(() => {
 async function prepare() {
  try {
    await SplashScreen.preventAutoHideAsync();
    await LoadFonts();
    await fetchToken();
  } catch (e) {
     console.warn(e);
  } finally {
     setAppIsReady(true);
  }
}
prepare();
}, []);

const onLayoutRootView = useCallback(async () => {
 if (appIsReady) {
   await SplashScreen.hideAsync();
 }
}, [appIsReady]);

if (!appIsReady) {
 return null;
}

return (
 <NavigationContainer onReady={onLayoutRootView}>
   <MainNavigation />
 </NavigationContainer>
 );
};

export default function App() {
 return (
 <>
  <Provider store={store}>
    <ExpoStatusBar style="auto" />
    <Root />
  </Provider>
 </>
 );
}

Upvotes: 0

Annamalai D
Annamalai D

Reputation: 899

useSelector will work only if you wrap it inside Provider. you can create a wrapper file for App.

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

In App.js

const App = () => {
  const count = useSelector((state) => state.counter.count); // will Work!
}

Upvotes: 2

Kishan Bharda
Kishan Bharda

Reputation: 5700

As error saying, you are using useSelector out side of provider. In your app.js you are using useSelector before the app renders, so it is not able to find store. So, create a component for functionality which you want to use in app.js like this :

Create a file, call it anything like CountView.js, in CountView.js use your redux login : CountView.js

import React from 'react';
import { Text } from 'react-native';
import { useSelector } from 'react-redux';

const CountView = () => {
  const count = useSelector((state) => state.counter.count);

  return (
    <Text>{`ha ${count}`}</Text>
  )
}

export default CountView;

Now, In your app.js use this component :

import React from 'react';
import { StyleSheet, Text, View } from 'react-native';
import Dogs from './components/Dogs';
import { Provider } from 'react-redux';
import store from './redux/configureStore'
import CountView from '../components/CountView'; // import CountView component

export default function App() {
  return (
    <Provider store={store}>
      <View style={styles.container}>
        {/* Use component here */}
        <CountView /> 
        <Dogs />
      </View>
    </Provider>
  );
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    backgroundColor: '#fff',
    alignItems: 'center',
    justifyContent: 'center',
  },
});

Keep other things as it is, and now your functionality will works.

Upvotes: 1

Related Questions