I am creating an app with Expo/React Native with Redux. I want to persist data. But all of data is not saving on AsyncStorage. There are 3 keys in store and 2 of them are array and one of them is string. String one is saving but arrays not. You can find all my code in https://github.com/yunisdev/walllet . Can you please help me to fix this problem?
You are using Expo, so you should use the ExpoFileSystemStorage in order to store the state.
Like this:
import { AnyAction, applyMiddleware, combineReducers, createStore, Store } from "redux";
import ExpoFileSystemStorage from 'redux-persist-expo-filesystem';
import { persistStore, persistReducer } from 'redux-persist';
import thunk from 'redux-thunk';
const persistConfig = {
key: 'root',
storage: ExpoFileSystemStorage ,
whitelist: ['main']
};
const mainConfig = {
key: 'main',
storage: ExpoFileSystemStorage
}
import mainReducer from './reducer'
const rootReducer = combineReducers({
main: persistReducer(mainConfig, mainReducer)
})
const store: Store<any, AnyAction> = createStore(persistReducer(persistConfig, rootReducer), applyMiddleware(thunk))
const persistor = persistStore(store)
export {
store,
persistor
}
Related
I’ve been struggling on this problem for while n now it’s a pretty simple issue I need a way to persist my data from redux into react native’s local storage I have tried using multiple approaches like the redux-persist library but I still have not been able to crack it… has anyone found a solution to this issue, I’m open to all approaches.
Here is the code I used for the redux-persist library
store.ts
import {createStore, combineReducers, applyMiddleware} from 'redux';
import thunk from 'redux-thunk';
import {persistStore} from 'redux-persist';
import authReducer from './reducers/authReducer';
import toastReducer from './reducers/toastReducer';
const rootReducer = combineReducers({
toast: toastReducer,
auth: authReducer,
});
export const Store = createStore(rootReducer, applyMiddleware(thunk));
export const PersistedStore = persistStore(Store);
export type RootState = ReturnType<typeof Store.getState>;
export type AppDispatch = typeof Store.dispatch;
authReducer.ts
import AsyncStorage from '#react-native-async-storage/async-storage';
import {persistReducer} from 'redux-persist';
import {AuthResponse} from '../../types/AuthResponse';
import {AuthReduxAction} from '../../types/ReduxActions';
import {SET_USER_DETAILS} from '../constants';
const persistConfig = {
key: 'root',
storage: AsyncStorage,
};
interface State {
userDetails: AuthResponse;
}
const initialState: State = {
userDetails: {},
};
const authReducer: (
state: State,
action: AuthReduxAction,
) => State | Promise<State> = async (state = initialState, action) => {
switch (action.type) {
case SET_USER_DETAILS:
// Saving user details to local storage
return {...state, userDetails: action.payload};
default:
return state;
}
};
export default persistReducer(persistConfig, authReducer);
App.tsx
import React from 'react';
import Routes from './Routes';
import {Provider} from 'react-redux';
import {PersistGate} from 'redux-persist/integration/react';
import {LogBox} from 'react-native';
import {PersistedStore, Store} from './redux/store';
LogBox.ignoreLogs(['new NativeEventEmitter']);
const App = () => {
return (
<Provider store={Store}>
<PersistGate loading={null} persistor={PersistedStore}>
<Routes />
</PersistGate>
</Provider>
);
};
export default App;
Here is the response I get when I print out the data (auth data in this case) from the redux store:
{"_U": 0, "_V": 0, "_W": null, "_X": null, "_persist": {"rehydrated": true, "version": -1}}
Any help would be appreciated
Thanks
I would like to do redux-persist this way and it works for several projects. Check it out if it works for you.
store.js
import { applyMiddleware, createStore, compose } from "redux";
import { persistStore, persistReducer } from "redux-persist";
import AsyncStorage from "#react-native-async-storage/async-storage";
import thunkMiddleware from "redux-thunk";
import { createLogger } from "redux-logger";
import reducer from "./reducers/";
const enhancers = [
applyMiddleware(
thunkMiddleware,
createLogger({
collapsed: true,
// eslint-disable-next-line no-undef
predicate: () => __DEV__,
})
),
];
/* eslint-disable no-undef */
const composeEnhancers =
(__DEV__ &&
typeof window !== "undefined" &&
window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__) ||
compose;
/* eslint-enable no-undef */
const enhancer = composeEnhancers(...enhancers);
const persistConfig = {
key: "root",
storage: AsyncStorage,
blacklist: [],
};
const persistedReducer = persistReducer(persistConfig, reducer);
export const store = createStore(persistedReducer, {}, enhancer);
export const persistor = persistStore(store);
authReducer.js
import {AuthResponse} from '../../types/AuthResponse';
import {AuthReduxAction} from '../../types/ReduxActions';
import {SET_USER_DETAILS} from '../constants';
interface State {
userDetails: AuthResponse;
}
const initialState: State = {
userDetails: {},
};
const authReducer: (
state: State,
action: AuthReduxAction,
) => State | Promise<State> = async (state = initialState, action) => {
switch (action.type) {
case SET_USER_DETAILS:
// Saving user details to local storage
return {...state, userDetails: action.payload};
default:
return state;
}
};
export default authReducer;
index.js (same location as authReducer.js)
import { combineReducers } from 'redux';
import authReducer from './authReducer';
import toastReducer from './toastReducer';
export default combineReducers({
authReducer ,
toastReducer
});
I am using react-native with redux-persist and I cannot seem to get my store to persist into async storage. According to the redux-persist github repo, this may have to do with AsyncStorage being removed from react-native, but I cannot seem to figure out what needs to be done to fix the issue.
When my app launches I see the actions persist/PERSIST and persist/REHYDRATE fire in my redux devtools, but nothing ever is stored in async storage.
I thought I had set up my file as the redux-persist docs explained but I imagine I am missing something. I'm fairly new to Redux as it is so I may just be confused on something basic.
My Redux.config.js file ...
import React from 'react';
import { createStore, combineReducers, applyMiddleware, compose } from 'redux';
import { Provider } from 'react-redux'
import thunk from 'redux-thunk';
import { initialState } from './initialState';
import { puppyReducer } from './reducers/puppies/puppyReducer';
import { uiReducer } from './reducers/ui/uiReducer';
import { userReducer } from './reducers/user/userReducer';
import { contentReducer } from './reducers/content/contentReducer';
import { persistStore, persistReducer, persistCombineReducers } from 'redux-persist';
import storage from 'redux-persist/lib/storage'
import logger from 'redux-logger';
import Navigation from './Navigation.config';
import { PersistGate } from 'redux-persist/integration/react';
const persistConfig = {
key: 'root',
storage: storage
}
const rootReducer = {
dogs: puppyReducer,
ui: uiReducer,
user: userReducer,
content: contentReducer
};
const reducer = persistCombineReducers(persistConfig, rootReducer);
const persistedReducer = persistReducer(persistConfig, reducer);
const composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose;
const store = createStore(persistedReducer, initialState, composeEnhancers(applyMiddleware(thunk), applyMiddleware(logger)));
const persistor = persistStore(store);
// Wrap the redux State Provider around the Main Navigation Stack
function StateWrapper() {
return (
<Provider store={store}>
<PersistGate loading={null} persistor={persistor}>
<Navigation />
</PersistGate>
</Provider >
)
}
export default App = StateWrapper;
I expect that this should load my persisted state when the app launches, and continue to update that persisted state as my redux state changes. As it stands currently, my state is updating fine, but nothing is persisted. There are no error messages showing.
I persist my redux store using AsyncStorage. Rather than using redux-persist's storage option, I have installed AsyncStorage from the #react-native-community. Just make sure that you link the dependency properly.
Then in my config.js file I import and use AsyncStorage as follows.
import AsyncStorage from '#react-native-community/async-storage';
const config = {
key: 'primary',
keyPrefix: '', // the redux-persist default `persist:` doesn't work with some file systems
storage: AsyncStorage,
};
Your issue could also be related to the keyPrefix try setting it to some value other than the default.
Try the following:
const reducer = persistCombineReducers(persistConfig, rootReducer);
const persistedReducer = persistReducer(persistConfig, reducer rootReducer);
This works for me:
//...
import {combineReducers} from 'redux';
import {persistReducer} from 'redux-persist';
const persistConfig = {
key: 'root',
storage: storage
}
const rootReducer = {
dogs: puppyReducer,
ui: uiReducer,
user: userReducer,
content: contentReducer
};
const persistedReducer = persistReducer(
persistConfig,
combineReducers(rootReducers)
);
//...
Right now I'm trying to integrate redux persist to my redux-saga in react native. I'm already trying some code in CreateStore. My app can run, but the reducer always reset after reloading the app.
This is my code
// CreateStore.js
import { applyMiddleware, compose } from 'redux'
import createSagaMiddleware from 'redux-saga'
import { persistStore, persistCombineReducers } from 'redux-persist';
import { AsyncStorage } from 'react-native';
import Reactotron from "reactotron-react-native";
const config = {
key: 'root',
storage: AsyncStorage,
};
// creates the store
export default (rootReducer, rootSaga) => {
/* ------------- Redux Configuration ------------- */
const middleware = []
const enhancers = []
/* ------------- Saga Middleware ------------- */
const sagaMiddleware = createSagaMiddleware()
middleware.push(sagaMiddleware)
/* ------------- Assemble Middleware ------------- */
enhancers.push(applyMiddleware(...middleware))
const reducers = persistCombineReducers(config, rootReducer);
const persistConfig = { enhancers };
const store = Reactotron.createStore(rootReducer, compose(...enhancers));
persistStore(store);
// kick off root saga
sagaMiddleware.run(rootSaga)
return { store };
}
// app.js
import React, { Component } from "react";
import { Provider } from "react-redux";
import Reactotron from "reactotron-react-native";
import createStore from '../src/Redux'
import PrimaryNav from "../src/navigations/AppNavigations";
export default class App extends Component {
render() {
const { store } = createStore()
console.log = Reactotron.log
console.disableYellowBox = true;
return (
<Provider store={store}>
<PrimaryNav />
</Provider>
);
}
}
Does anyone have a clue for solving this issue? I want the reducer keep the previous data before reloading the app.
Thanks, Everyone
Ok, I finally can use redux persist with redux saga.
This is my last code
import { applyMiddleware, compose } from 'redux'
import createSagaMiddleware from 'redux-saga'
import Reactotron from "reactotron-react-native";
import { persistStore, persistReducer } from 'redux-persist';
import storage from 'redux-persist/lib/storage';
const persistConfig = {
key: 'root',
storage,
}
// creates the store
export default (rootReducer, rootSaga) => {
/* ------------- Redux Configuration ------------- */
const middleware = []
const enhancers = []
/* ------------- Saga Middleware ------------- */
const sagaMiddleware = createSagaMiddleware()
middleware.push(sagaMiddleware)
/* ------------- Assemble Middleware ------------- */
enhancers.push(applyMiddleware(...middleware))
const persistedReducer = persistReducer(persistConfig, rootReducer)
const store = Reactotron.createStore(persistedReducer, compose(...enhancers));
const persistor = persistStore(store)
// kick off root saga
sagaMiddleware.run(rootSaga)
return { store, persistor };
}
I have been using react-native-router-flux in my app and configured the store and it works like a charm.
I am now planning to use react-navigation v2.8 in my app and i am struggling to complete the configureStore.
I supposed that the configureStore would be used as is from the react-native-router-flux example. Below is the sample code from my working react-native-router-flux sample.
/* global __DEV__ */
import { Router } from 'react-native-router-flux'; //this is not to be used
in react-navigation
import { connect } from 'react-redux';
import { createStore, applyMiddleware, compose } from 'redux';
import { persistStore } from 'redux-persist';
import ReduxPromise from 'redux-promise';
import thunk from 'redux-thunk';
import { createLogger } from 'redux-logger';
import reducers from './index';
connect()(Router); //ROuter is not avaible in react-navigation
let middleware;
if (__DEV__) {
const logger = createLogger();
middleware = [thunk, ReduxPromise, logger];
//middleware = [thunk, ReduxPromise];
} else {
middleware = [thunk, ReduxPromise];
}
export function configureStore() {
const store = compose(applyMiddleware(...middleware))(
createStore
)(reducers);
const persistor = persistStore(store);
return { persistor, store };
}
export const { persistor, store } = configureStore();
React Navigation can be used as a general JSX Component that can be enclosed within the Provider and thus the whole UI tree will have the store available inside the context. So you can just initialize the store in your app's root, keep it as a state variable inside
it and pass it to the context using Provider.
Code for configuring your store and making it available down in your UI tree made using React Navigation should look like below:
Your Root component should look like this
import React, {Component} from 'react';
import {Provider} from 'react-redux';
import AppStack from './app/AppNavigation';
import getStore from './app/store';
export default class App extends Component {
constructor (props) {
super(props);
this.state = {
store : getStore()
}
}
render() {
return (
<Provider store={this.state.store} >
<AppStack /> . // This is your React Navigation Stack.
</Provider>
);
}
}
and your getStore should look like this:
import { createStore } from 'redux';
import reducers from './reducers';
export default function getStore () {
const enhancer = compose(
applyMiddleware(
//your middlewares here..
)
);
const store = createStore(reducers);
return store;
}
and the AppStack can look like this:
import {createStackNavigator} from 'react-navigation';
import FirstScreen from './components/FirstScreen';
export default AppStack = createStackNavigator({
Screen1: {
screen: FirstScreen,
//other routes....
},
});
This is just a mere simplest possible example.
I am trying to integrate redux navigation to my existing app. It shows me the error No reducer provided for key navReducer. Any help is appreciated. Other stackoverflow answers point to the fact that there is problem in importing when this kind of error occurs but I am not sure why this is occuring in my case.
My /components/navigation.js:
import React, { Component } from 'react'
import { createStackNavigator } from 'react-navigation'
import Login from '../containers/loginContainer'
import Home from '../containers/homeContainer'
import {
reduxifyNavigator,
createReactNavigationReduxMiddleware,
createNavigationReducer,
} from 'react-navigation-redux-helpers'
export const RootStack = createStackNavigator(
{
Login: {
screen: Login,
},
Home: {
screen: Home,
},
},
{
initialRouteName: 'Login'
}
)
export const navReducer = createNavigationReducer(RootStack)
export const navigationMiddleware = createReactNavigationReduxMiddleware(
"root",
state => state.nav,
)
const appNavigator = reduxifyNavigator(RootStack, "root")
const mapStateToProps = (state) => ({
state: state.nav,
})
export const AppWithNavigationState = connect(mapStateToProps)(appNavigator)
My store configuration is:
import { createStore, applyMiddleware, compose } from 'redux'
import { persistStore, persistReducer } from 'redux-persist'
import thunk from 'redux-thunk'
import storage from 'redux-persist/lib/storage'
import rootReducer from './reducers'
import { navigationMiddleware } from './components/navigation'
const persistConfig = {
key: 'myKey',
storage,
}
const middlewares = [thunk, navigationMiddleware]
const persistedReducer = persistReducer(persistConfig, rootReducer)
const store = createStore(
persistedReducer,
{},
compose(
applyMiddleware(...middlewares)
)
)
const persistedStore = persistStore(store)
export default store
My root reducer looks something like this:
import { combineReducers } from 'redux';
import { navReducer } from '../components/navigation'
const rootReducer = combineReducers({
navReducer, // It throws the error here!!
})
export default rootReducer