How to solve "Unable to resolve module" issue with react-redux? - react-native

I'm building a react-native app with Expo, when I doing refactor job and suddenly I can't open my app with Expo client, it shows an error
TypeError: (0, _redux.combinReducers) is not a function. (In '(0, _redux.combineReducers)(reducers)', '(0, _redux.combineReducers)' is undefined)
after some researching I see some reason cause this issue is cause the same folder name with redux, which I made this mistake also, so I change the folder name to "appRedux", after that the error message change to
Unable to resolve "../../../../src/redux" from "node_modules\react-redux\lib\connect\mapDispatchToProps.js"
I'm sure I've wrapped my App component in Provider as this solution said, but just no luck to solve it.
I've been stuck in here for few days, please help me to solve this issue, if need more info please let me know, any suggestion is appreciated.
App.js
import React, { Component } from 'react';
import Navigation from './src/navigation'
import { Provider } from 'react-redux'
import { getStore, getPersistor } from './src/store/configureStore'
import { PersistGate } from 'redux-persist/integration/react'
import { SafeAreaProvider } from 'react-native-safe-area-context'
import { ApolloProvider } from '#apollo/react-hooks'
import { getClient } from './src/services/GraphQL/Client'
const store = getStore()
const persistor = getPersistor()
const client = getClient()
export default class App extends Component {
render() {
return (
<ApolloProvider client={client}>
<Provider store={store} >
<PersistGate persistor={persistor}>
<SafeAreaProvider>
<Navigation />
</SafeAreaProvider>
</PersistGate>
</Provider>
</ApolloProvider>
);
}
}
configureStore.js
import { createStore, applyMiddleware } from 'redux'
import { persistStore } from 'redux-persist'
import reducers from '#redux'
import thunk from 'redux-thunk'
const store = createStore(reducers, applyMiddleware(thunk))
const persistor = persistStore(store)
const getPersistor = () => persistor
const getStore = () => store
export {
getStore,
getPersistor
}
Reducers.js
import { combineReducers } from 'redux'
import { persistCombineReducers, persistReducer } from 'redux-persist'
import { AsyncStorage } from 'react-native'
import carts from './carts'
import app from './app'
import user from './user'
const rootConfig = {
key: 'root',
storage: AsyncStorage,
blacklist: [
'app',
'carts',
'user'
]
}
const appConfig = {
key: 'app',
storage: AsyncStorage,
blacklist: [
'selectedDate',
'selectedDateIndex',
'isFetching',
'showFilter'
]
}
const cartConfig = {
key: 'carts',
storage: AsyncStorage,
blacklist: [
'isFetching',
'error'
]
}
const userConfig = {
key: 'user',
storage: AsyncStorage,
blacklist: [
'isFetching',
'error',
'history'
]
}
export default persistCombineReducers(rootConfig, {
app: persistReducer(appConfig, app),
carts: persistReducer(cartConfig, carts),
user: persistReducer(userConfig, user)
})

Finally I solve this issue by start expo client with
expo start -c
which will start expo app with clearing the cache, hope this will help someone facing the same issue

Related

Redux toolkit persist, My authentication is not persisted react-native expo

So I have looked through this site and I have come across interesting questions post that I have implemented and have no results. So please have a look at the code and direct me. This is what my store looks like and please bear in mind that I am using a package called reduxjs-toolkit-persist
import { configureStore } from "#reduxjs/toolkit";
import AsyncStorage from "#react-native-async-storage/async-storage";
import {
persistCombineReducers,
persistStore,
FLUSH,
REHYDRATE,
PAUSE,
PERSIST,
PURGE,
REGISTER,
} from "reduxjs-toolkit-persist";
import authSlice from "./authSlice";
import userSlice from "./userSlice";
const persistConfig = {
key: "root",
storage: AsyncStorage,
whitelist: ["authUser", "loginUser"],
};
const _persistedReducer = persistCombineReducers(persistConfig, {
auth: authSlice,
user: userSlice,
});
const store = configureStore({
reducer: { _persistedReducer },
middleware: (getDefaultMiddleware) =>
getDefaultMiddleware({
serializableCheck: {
ignoredActions: [FLUSH, REHYDRATE, PAUSE, PERSIST, PURGE, REGISTER],
},
serializableCheck: false,
}),
});
export const persistor = persistStore(store);
export default store;
and this is what my app.js looks like
import { StatusBar } from "expo-status-bar";
import { Router } from "./src/navigation/router";
import { Provider } from "react-redux";
import store, { persistor } from "./src/store";
import { LogBox } from "react-native";
import { PersistGate } from "reduxjs-toolkit-persist/integration/react";
LogBox.ignoreLogs([
"AsyncStorage has been extracted from react-native core and will be removed in a future release.",
]);
export default function App() {
return (
<Provider store={store}>
<PersistGate loading={null} persistor={persistor}>
<Router />
</PersistGate>
<StatusBar style="auto" animated />
</Provider>
);
}
I have followed the documentation but I don't get desired results. I don't know if it is storage problem or a middleware problem

Redux-persist not persisting with expo on reload

I am using following tech stack
react-native
expo
redux
redux-persist.
I have tried articles, docs as well as questions here also but not help. Its simply not persisting. I am using emulator and running through expo only. As soon i close the app and reload expo client it simply doesn't persist and ask me to login again.
I also tried using AsyncStorage but still not working.
Hers's my code:
index.js
import thunk from "redux-thunk";
import AsyncStorage from "#react-native-community/async-storage";
import ExpoFileSystemStorage from "redux-persist-expo-filesystem"
// import storage from 'redux-persist/lib/storage';
import { createStore, applyMiddleware, compose } from "redux";
import { persistStore, persistReducer } from "redux-persist";
import rootReducer from "./reducers/rootReducer";
import logger from "redux-logger";
const persistConfig = {
key: "root",
storage: ExpoFileSystemStorage,
whitelist: ["authReducer"],
};
const composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose;
const persistedReducer = persistReducer(persistConfig, rootReducer);
const store = createStore(
persistedReducer,
composeEnhancers(applyMiddleware(thunk, logger))
);
let persistor = persistStore(store);
export { store, persistor };
app.js
import React from "react";
import { Provider } from "react-redux";
import { PersistGate } from "redux-persist/integration/react";
import { AppLoading } from "expo";
import { useFonts } from "#use-expo/font";
import { NavigationContainer } from "#react-navigation/native";
import { ThemeProvider } from "react-native-elements";
import { theme } from "./constants/ThemeConfiguration";
import { store, persistor } from "./store";
import RootNavigator from "./navigators/RootNavigator";
export default (App) => {
let [fontsLoaded] = useFonts({
"Lato-Regular": require("./assets/fonts/Lato-Regular.ttf"),
});
if (!fontsLoaded) {
return <AppLoading />;
} else {
return (
<Provider store={store}>
<PersistGate loading={null} persistor={persistor}>
<ThemeProvider theme={theme}>
<NavigationContainer>
<RootNavigator />
</NavigationContainer>
</ThemeProvider>
</PersistGate>
</Provider>
);
}
};
rootReducer.js
import thunk from "redux-thunk";
import AsyncStorage from "#react-native-community/async-storage";
import { createStore, applyMiddleware, compose } from "redux";
import { persistStore, persistReducer } from "redux-persist";
import rootReducer from "./reducers/rootReducer";
import logger from "redux-logger";
const persistConfig = {
key: "auth",
storage: AsyncStorage,
};
const composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose;
const persistedReducer = persistReducer(persistConfig, rootReducer);
const store = createStore(
persistedReducer,
composeEnhancers(applyMiddleware(thunk, logger))
);
let persistor = persistStore(store);
export { store, persistor };
I got it fixed. Its not there in any documentation or article. The problem is with the persistConfig. Key which is root here. It should be the name of the reducer which we want to persist. In my case it was auth.
Updated persistConfig will be as follows:
const persistConfig = {
key: "auth",
storage: AsyncStorage,
};

Can't get Jest expo app to work with react-navigation

I am trying to snapshot test with Jest, Expo, React Navigation and my whole app uses hooks only. I'd like to eventually make these into e2e tests where Jest clicks through and snapshot tests everything but I can't even get react navigation to render. My snapshot after the expo loader always shows "null." I followed the basic example from the tabs starter that comes with expo init but the way it outlines how to setup the mocks simply doesn't work for my app. I've tried all sorts of things but nothing works.
App.tsx
import { Ionicons } from '#expo/vector-icons';
import { AppLoading } from 'expo';
import { Asset } from 'expo-asset';
import * as Font from 'expo-font';
import React, { useState } from 'react';
import { YellowBox } from 'react-native';
import { Provider as PaperProvider } from 'react-native-paper';
import { useScreens } from 'react-native-screens';
import { Provider as RxProvider } from 'reactive-react-redux';
import { applyMiddleware, compose, createStore } from 'redux';
import thunk from 'redux-thunk';
import SnackBar from './components/UI/Snackbar';
import { socketMiddleware } from './lib/socketMiddleware';
import SwitchNavigator from './navigation/AppNavigator';
import rootReducer from './reducers/rootReducer';
import { theme } from './Theme';
const middleware = [thunk, socketMiddleware()];
const composeEnhancers = (window as any).__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose;
export const store = createStore<iAppState, any, any, any>(
rootReducer,
composeEnhancers(applyMiddleware(...middleware))
);
// Must be called prior to navigation stack rendering
useScreens();
YellowBox.ignoreWarnings(['Require cycle:']);
const App = (props) => {
const [isLoadingComplete, setLoadingComplete] = useState(false);
if (!isLoadingComplete && !props.skipLoadingScreen) {
return (
<AppLoading
startAsync={loadResourcesAsync}
onError={handleLoadingError}
onFinish={() => handleFinishLoading(setLoadingComplete)}
/>
);
} else {
return (
<RxProvider store={store}>
<PaperProvider theme={theme}>
<SwitchNavigator />
<SnackBar />
</PaperProvider>
</RxProvider>
);
}
};
const loadResourcesAsync = async () => {
await Promise.all([
Asset.loadAsync([
//nothing
]),
Font.loadAsync({
...Ionicons.font,
TitilliumText250: require('./assets/fonts/TitilliumText22L-250wt.otf'),
TitilliumText800: require('./assets/fonts/TitilliumText22L-800wt.otf')
})
]);
};
const handleLoadingError = (error: Error) => {
console.warn(error);
};
const handleFinishLoading = (setLoadingComplete) => {
setLoadingComplete(true);
};
export default App;
App.test.tsx:
import React from 'react';
import { Provider as PaperProvider } from 'react-native-paper';
import NavigationTestUtils from 'react-navigation/NavigationTestUtils';
import renderer from 'react-test-renderer';
import App from './App';
import { theme } from './Theme';
jest.mock('expo', () => ({
AppLoading: 'AppLoading'
}));
jest.mock('react-native-screens');
jest.mock('react-native-paper');
jest.mock('redux');
jest.mock('reactive-react-redux');
jest.mock('./navigation/AppNavigator', () => 'SwitchNavigator');
describe('App', () => {
jest.useFakeTimers();
beforeEach(() => {
NavigationTestUtils.resetInternalState();
});
// success
it(`renders the loading screen`, () => {
const tree = renderer.create(<App />).toJSON();
expect(tree).toMatchSnapshot();
});
// this snapshot is always null
it(`renders the root without loading screen`, () => {
const tree = renderer
.create(
<PaperProvider theme={theme}>
<App skipLoadingScreen></App>
</PaperProvider>
)
.toJSON();
expect(tree).toMatchSnapshot();
});
});
/navigation/AppNavigator.tsx:
import { createAppContainer, createSwitchNavigator } from 'react-navigation';
import LoginStack from './LoginStack';
import TabStack from './TabStack';
/** The most root navigator which allocates to the others. */
const SwitchNavigator = createAppContainer(
createSwitchNavigator(
{
LoginStack: LoginStack,
TabStack: TabStack
},
{
initialRouteName: 'LoginStack'
}
)
);
export default SwitchNavigator;
I was having a similar issue and found a fix via: https://github.com/expo/expo/issues/7155#issuecomment-592681861
Seems like the act() worked magically for me to stop it from returning null (not sure how)
Update your test to use it like this:
import { act, create } from 'react-test-renderer';
it('renders the root without loading screen', () => {
let tree;
act(() => {
tree = create(
<PaperProvider theme={theme}>
<App skipLoadingScreen></App>
</PaperProvider>
);
});
expect(tree.toJSON()).toMatchSnapshot();
});
Note: When I changed how I was using the imports from 'react-test-renderer' my tests couldn't find act() as a function, a simple re-install of npm packages solved this problem!

redux-persist: undefined is not an object (evaluating store.getState)

It's my first time using Redux and Redux Persist.
I got this error when I tried to run my application (in index.js line 20):
TypeError: undefined is not an object (evaluating 'store.getState')
index.js:
import React from 'react';
import { AppRegistry } from 'react-native';
import App from './App';
import { name as appName } from './app.json';
import { Provider } from 'react-redux';
import {persistor, store}from './store/configureStore';
import { PersistGate } from 'redux-persist/integration/react';
const RNRedux = () => (
<Provider store={store}>
<PersistGate loading={null} persistor={persistor}>
<App/>
</PersistGate>
</Provider>
)
AppRegistry.registerComponent(appName, () => RNRedux);
App.js:
import {createStackNavigator, createAppContainer} from 'react-navigation';
import Register from './components/Register';
import Home from './components/Home';
import Login from './components/Login';
const AppNavigator = createStackNavigator({
HomeScreen: {
screen: Home,
navigationOptions: {
title: 'Home',
header: null
}
},
LoginScreen: {screen: Login},
RegisterScreen: {
screen: Register,
navigationOptions: {
title: 'Register'
}
}
});
const App = createAppContainer(AppNavigator);
export default App;
configureStore.js:
import { createStore, combineReducers } from 'redux';
import userReducer from './reducers/user';
import { persistStore, persistCombineReducers, persistReducer } from 'redux-persist';
import storage from 'redux-persist/es/storage' // default: localStorage if web, AsyncStorage if react-native
import { AsyncStorage } from 'react-native';
const persistConfig = {
key: 'root',
storage
};
const rootReducer = persistCombineReducers(persistConfig, {
user: userReducer
});
const persistedReducer = persistReducer(persistConfig, rootReducer);
export default () => {
let store = createStore(persistedReducer)
let persistor = persistStore(store, {storage: AsyncStorage})
return { store, persistor };
}
You are exporting a function from configureStore.js but trying to import {persistor, store} in index.js.
You should import the function instead and call it.
For example:
import configureStore from './store/configureStore';
const {store, persistor} = configureStore();
import thunk from "redux-thunk";
import { persistStore, persistReducer } from "redux-persist";
import { AsyncStorage } from "react-native";
import logger from "redux-logger";
const persistConfig = {
key: "root",
storage: AsyncStorage,
whitelist: ["authReducer"],
};
const persistedReducer = persistReducer(persistConfig, rootReducer);
const middleWares = [thunk, logger];
const store = createStore(persistedReducer, applyMiddleware(...middleWares));
let persistor = persistStore(store);
export default { store, persistor };
I was getting the same error "undefined is not an object (evaluating 'store.getState')"
My issue was solved after I removed the default keyword after export
export { store, persistor };

Redux, Redux-persist, cannot read property "age" of undefined

I am using Redux with a React Native app, and am getting a red box error when trying to integrate Redux-Persist. As soon as I navigate to the initial screen which has a ProfileForm and has the this.props.age piece of state, I see this:
TypeError: Cannot read property "age" of undefined
I am not quite sure where I am going wrong. I have set initial value of age = 0 in the reducer. So how is it undefined? Here is code that would be relevant:
reducers/UserReducer.js
import { CALCULATE_BMI, CALCULATE_AGE } from '../actions/types';
const INITIAL_STATE = { bmi: 0, age: 0 };
export default (state = INITIAL_STATE, action) => {
switch (action.type) {
case CALCULATE_BMI:
return { ...state, bmi: action.bmi };
case CALCULATE_AGE:
return { ...state, age: action.age };
default:
return state;
}
};
reducers/index.js
import { combineReducers } from 'redux';
import { reducer as formReducer } from 'redux-form';
import userReducer from './UserReducer';
export default combineReducers({
user: userReducer,
form: formReducer,
});
store/index.js
import { createStore, applyMiddleware, compose } from 'redux';
import { persistStore, persistCombineReducers } from 'redux-persist';
import storage from 'redux-persist/es/storage'; // default: localStorage if web, AsyncStorage if react-native
import thunk from 'redux-thunk';
import logger from 'redux-logger';
import reducers from '../reducers';
const config = {
key: 'root',
storage,
};
const reducer = persistCombineReducers(config, { reducers });
const composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose;
export default () => {
const store = createStore(reducer, {}, composeEnhancers(applyMiddleware(thunk, logger)));
const persistor = persistStore(store);
return { persistor, store };
};
ProfileForm.js
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import { StyleSheet, Text, View, TouchableOpacity, Alert } from 'react-
native';
import { compose } from 'redux';
import { connect } from 'react-redux';
import { reduxForm, Field } from 'redux-form';
import * as actions from '../actions';
import DatePickerModal from '../components/DatePickerModal';
class ProfileForm extends Component {
// other code here
<Field name="birthdate" component={DatePickerModal} />
{this.props.age === 0 ? null : (
<Text style={styles.labelStyle}>{`Age: ${this.props.age}`}</Text>
)}
//other code here
}
const mapStateToProps = state => ({
age: state.user.age,
});
export default compose(connect(mapStateToProps, actions), reduxForm({ form: 'Profile', validate }))(
ProfileForm,
);
App.js
import React, { Component } from 'react';
import { Provider } from 'react-redux';
import { PersistGate } from 'redux-persist/es/integration/react';
import MainNavigator from './routes';
import configureStore from './store';
const { persistor, store } = configureStore();
export default class App extends Component {
// other code here
render() {
return (
<Provider store={store}>
<PersistGate persistor={persistor}>
<MainNavigator />
</PersistGate>
</Provider>
);
}
}
I have tried removing the empty object {} passed to createStore, setting it as undefined, and setting the initial values here instead, but nothing seems to work. Any guidance appreciated.