I am trying to figure put how to use Redux-Persist. I have the following code in my index.js:
import { AppRegistry, View } from "react-native";
import React from "react";
import App from "./App";
import { name as appName } from "./app.json";
import { Provider } from "react-redux";
import { PersistGate } from "redux-persist/integration/react";
import storeConfig from "./app/config/store";
const AppRedux = () => (
<Provider store={storeConfig.store}>
<PersistGate presistor={storeConfig.persistor} loading={null}>
<App />
</PersistGate>
</Provider>
);
AppRegistry.registerComponent(appName, () => AppRedux);
When I run it I get the following message:
Unhandled JS Exception: TypeError: Cannot read property 'subscribe' of undefined
BUT, When i comment out PersistGate I have no problems and my store gets rehydrated.
The reason I am trying to keep PersistGate is to have splash screen while rehydrating. Any suggestions on how to solve this problem?
P.S. This is what I have in store file:
export default () => {
const pReducer = persistReducer(persistConfig, reducers);
const store = createStore(pReducer, {}, applyMiddleware(logger));
const persistor = persistStore(store);
return { store, persistor };
};
Related
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
I understand this kind of question was already asked several times here at StackOverflow. But I tried all the recommended solutions and nothing works for me. I'm running out of ideas.
The problem is with a React Native application for Android. Basically, the app provides a search bar to search an underlying database. The search results should be put into the store.
I use Redux v4.0.5, React-Redux v7.1.3, React v16.12.0 and React Native v0.61.5. For debugging, I use React Native Debugger in the latest version.
Now the simplified code. First, the component with the search bar. Here, mapStateToProps() is called. User makes an input and useEffect() immediately runs the database query, which should result in immediately calling mapStateToProps().
import React, {useEffect, useRef, useState} from 'react';
import {connect} from 'react-redux';
import {RootState} from '../../../rootReducer/rootReducer';
import {setResultValueSearchBar} from '../../../store/searchBar/actions';
imports ...
type Props = {};
const SearchBar: React.FC<Props> = () => {
const [returnValue, setReturnValue] = useState('');
const [inputValue, setInputValue] = useState('');
useEffect(() => {
// get query results
// logic to finally get a result string that should be put into the store
const resultNames: string = resultNamesArray.toString();
// method to set local and Redux state
const sendReturnValueToReduxStore = (resultNames: string) => {
setReturnValue(resultNames);
setResultValueSearchBar({resultValue: resultNames});
console.log('result value sent to store ', resultNames);
};
// call above method
sendReturnValueToReduxStore(resultNames);
}, [inputValue, returnValue]);
return (
<View>
<ScrollView>
<Header searchBar>
<Item>
<Input
placeholder="Search"
onChangeText={text => setInputValue(text)}
value={inputValue}
/>
</Item>
</Header>
</ScrollView>
</View>
);
};
function mapStateToProps(state: RootState) {
console.log("map state to props!", state); // is only called one time, initially
return {
resultValue: state.searchBarResult.resultValue,
};
}
const mapDispatchToProps = {
setResultValueSearchBar,
};
export default connect(mapStateToProps, mapDispatchToProps)(SearchBar);
Here is the rootReducer:
import {combineReducers} from 'redux';
import searchBarResultReducer from '../store/searchBar/reducers';
import reducer2 from '../store/reducer2example/reducers';
const rootReducer = combineReducers({
searchBarResult: searchBarResultReducer,
reducer2Result: reducer2,
});
export type RootState = ReturnType<typeof rootReducer>;
Here is the searchBarResultReducer in reducers.ts file:
import {
SearchBarResultState,
SET_RESULT_VALUE_SEARCHBAR,
ResultValueType,
} from './types';
const initialState: SearchBarResultState = {
resultValue: 'No results',
};
// take state and action and then return a new state
function searchBarResultReducer(
state = initialState,
action: ResultValueType,
): SearchBarResultState {
console.log('invoked result: ', action.type); // called only initially
if (action.type === 'SET_RESULT_VALUE_SEARCHBAR') {
return {
...state,
...action.payload,
};
} else {
return state;
}
}
export default searchBarResultReducer;
And the corresponding types.ts ...
export const SET_RESULT_VALUE_SEARCHBAR = 'SET_RESULT_VALUE_SEARCHBAR';
export interface SearchBarResultState {
resultValue: string;
}
interface ResultValueAction {
type: typeof SET_RESULT_VALUE_SEARCHBAR;
payload: SearchBarResultState;
}
export type ResultValueType = ResultValueAction
... and the actions.ts:
import {SET_RESULT_VALUE_SEARCHBAR, ResultValueType, SearchBarResultState} from './types'
export const setResultValueSearchBar = (resultValue: SearchBarResultState): ResultValueType => ({
type: SET_RESULT_VALUE_SEARCHBAR,
payload: resultValue,
});
And index.js:
import React from 'react';
import {AppRegistry} from 'react-native';
import {createStore, applyMiddleware, compose} from 'redux';
import {Provider} from 'react-redux';
import App from './App';
import {name as appName} from './app.json';
import rootReducer from './src/rootReducer/rootReducer';
import Realm from 'realm';
import { composeWithDevTools } from 'redux-devtools-extension';
import invariant from 'redux-immutable-state-invariant';
const composeEnhancers = composeWithDevTools({});
const store = createStore(
rootReducer,
composeEnhancers(applyMiddleware(invariant()))
);
const Root = () => {
Realm.copyBundledRealmFiles();
return (
<Provider store={store}>
<App />
</Provider>
);
};
AppRegistry.registerComponent(appName, () => Root);
To summarize: Whenever the database query succeeds, the result value should be sent to the store. But in the React Native Debugger/Redux Devtools, the reducer/mapStateToProps() is called only once and only, as shown by the console.log s in the code.
What is going on here?
Solved! As stated by Hemant in this Thread, you also have to pass the action that you import as props into the component. Works like a charm now :)
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!
I am trying to use redux-promise-middleware in my app, but when I am trying to run my app error comes with an error message: 'middleware is not a function or middleware is null'.
I tried to use middleware without a function as suggested in redux async docs, but still error is coming.
configureStore.js
import { createStore, applyMiddleware } from 'redux'
import app from './reducers/index';
import PromiseMiddleware from 'redux-promise-middleware';
const configureStore = () => {
let middleware = applyMiddleware(PromiseMiddleware());
let store = createStore(app, middleware);
return store
};
export default configureStore;
index.js
import React from 'react'
import { AppRegistry } from 'react-native';
import App from './App';
import { Provider } from 'react-redux'
import configureStore from './configureStore'
const store = configureStore();
const ReduxMiddleware = () => {
<Provider store={store}>
<App />
</Provider>
}
AppRegistry.registerComponent('ReduxMiddleware', () => ReduxMiddleware);
Please anybody, suggest to me that why I am facing this error.
I am new to redux.
import promiseMiddleware from 'redux-promise-middleware';
composeStoreWithMiddleware = applyMiddleware(
promiseMiddleware,
)(createStore);
And not : PromiseMiddleware()
(not uppercase and w/o () )
I'm trying to use redux in my project I make my code like tutorial but it won't work and give error
I see most of the question about this error but I already done what they say the soultion like I should warp my root componet with provider or use connect
Invariant Violation: Could not find "store" in the context of "Connect(App)". Either wrap the root component in a <Provider>, or pass a custom React context provider to <Provider> and the corresponding React context consumer to Connect(App) in connect options.
index.js
import React from 'react';
import {AppRegistry} from 'react-native';
import {Provider} from 'react-redux';
import App from './App';
import { name as appName } from './app.json';
import placesReducer from './src/store/reducers/places';
import{createStore} from 'redux'
import configureStore from './src/store/configureStore';
const store = configureStore();
const RNRedux = () => (
<Provider store={store}>
<App/>
</Provider>
);
AppRegistry.registerComponent(appName, () => RNRedux);
APP.js
import React, { Component } from "react";
import { StyleSheet, View } from "react-native";
import { connect } from "react-redux";
class App extends Component {
render() {
return (
<View style={styles.container}>
some code
</View>
);
}
}
const mapStateToProps = state => {
return {
some code
};
};
const mapDispatchToProps = dispatch => {
return {
some code
};
};
export default connect(mapStateToProps, mapDispatchToProps)(App);
my store
import {combineReducers, createStore} from 'redux';
import placesReducer from './reducers/places';
const rootReducer = combineReducers({
places: placesReducer
});
const configureStore = () => createStore(rootReducer)
export default configureStore;
const configureStore = () => createStore(rootReducer)
configureStore in this case is a variable with the return value of createStore, you most likely want it to be a function.
do you have to call configureStore as callback?
can you call it directly like that:
const configureStore = createStore(rootReducer);