I have been getting the following error since sometime in my console, i have no idea what it means and why it is originating. Please spread some light on this matter.
it says:
persistReducer.js:50 Uncaught TypeError: action.rehydrate is not a function
at _rehydrate (persistReducer.js:50)
at persistReducer.js:54
redux-persist version on package.json: "^5.6.11"
locked version: "5.9.1"
Store configuration code:
import thunk from 'redux-thunk';
import { persistStore } from 'redux-persist';
import { History, createBrowserHistory } from 'history';
import { createUserManager, loadUser } from "redux-oidc";
import { routerReducer, routerMiddleware } from 'react-router-redux';
import { createStore, applyMiddleware, compose, combineReducers, GenericStoreEnhancer, Store, StoreEnhancerStoreCreator, ReducersMapObject } from 'redux';
import * as StoreModule from './reducers';
import { ApplicationState, reducers } from './reducers';
import userManager from "./utils/userManager";
// Create browser history to use in the Redux store
const baseUrl = document.getElementsByTagName('base')[0].getAttribute('href')!;
const history = createBrowserHistory({ basename: baseUrl });
export default function configureStore(history: History, initialState?: ApplicationState) {
// Build middleware. These are functions that can process the actions before they reach the store.
const windowIfDefined = typeof window === 'undefined' ? null : window as any;
// If devTools is installed, connect to it
const devToolsExtension = windowIfDefined && windowIfDefined.__REDUX_DEVTOOLS_EXTENSION__ as () => GenericStoreEnhancer;
const createStoreWithMiddleware = compose(
applyMiddleware(thunk, routerMiddleware(history)),
devToolsExtension ? devToolsExtension() : <S>(next: StoreEnhancerStoreCreator<S>) => next
)(createStore);
// Combine all reducers and instantiate the app-wide store instance
const allReducers = buildRootReducer(reducers);
const store = createStoreWithMiddleware(allReducers, initialState) as Store<ApplicationState>;
loadUser(store, userManager);
// Enable Webpack hot module replacement for reducers
if (module.hot) {
module.hot.accept('./reducers', () => {
const nextRootReducer = require<typeof StoreModule>('./reducers');
store.replaceReducer(buildRootReducer(nextRootReducer.reducers));
});
}
const persistor = persistStore(store);
return { store, persistor };
}
function buildRootReducer(allReducers: ReducersMapObject) {
return combineReducers<ApplicationState>(Object.assign({}, allReducers, { routing: routerReducer }));
}
// Get the application-wide store instance, prepopulating with state from the server where available.
const initialState = (window as any).initialReduxState as ApplicationState;
export const { store, persistor } = configureStore(history, initialState);
if you are working on localhost and using redux-devtools try to check Access File Url checkbox on extension options.
you can manage your chrome extensions by typing chrome://extensions/ in the address bar or by going to the setting and chose extensions from left menu.
I get this error when using redux devtools, remove this and it should go away.
window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__()
Your code is using API of an older redux-persist version.
Refer to Basic Usage for the updated API:
import { persistStore, persistReducer } from 'redux-persist'
const persistConfig = {
key: 'root',
storage,
}
const allReducers = persistReducer(persistConfig, buildRootReducer(reducers));
Related
I'm trying to set up redux with connected-react-router, but I didn't really understand how to do that, I'm using the create-react-app --template redux, but if I try to do that I get the following error: Uncaught Error: Actions must be plain objects. Use custom middleware for async actions.
import { configureStore, combineReducers } from "#reduxjs/toolkit";
import { connectRouter, routerMiddleware } from "connected-react-router";
import { createBrowserHistory } from "history";
import homePageSlice from "./reducers/homepage/homePageSlice";
export const history = createBrowserHistory();
export const store = configureStore({
reducer: {
router: connectRouter(history),
homepage: homePageSlice,
},
middleware: [routerMiddleware(history)],
});
setting middleware property will override the default middleware that configureStore injects.
Use this:
middleware: [
actionToPlainObject,
routerMiddleware(history)
]
and create actionToPlainObject.ts
import { Middleware } from '#reduxjs/toolkit';
import IStoreState from '../IStoreState';
export const actionToPlainObject: Middleware<IStoreState, any> = (store) => (
next
) => (action) => {
if (!action) {
return;
}
return next({ ...action });
};
If using class instances this allows to be passed to Redux.
I'm trying to implement Redux-persist on my React Native app. Following the setup docs exactly, I changed my store.js from this:
import { applyMiddleware, createStore } from 'redux';
import * as thunkMiddleware from 'redux-thunk';
import reducers from '../reducers';
let middlewares = [thunkMiddleware.default];
const store = createStore(reducers, applyMiddleware(...middlewares));
export default store;
To this:
import { applyMiddleware, createStore } from 'redux';
import * as thunkMiddleware from 'redux-thunk';
import { persistStore, persistReducer } from 'redux-persist';
import AsyncStorage from '#react-native-community/async-storage';
import reducers from '../reducers';
const persistConfig = {
key: 'root',
storage: AsyncStorage,
};
const persistedReducer = persistReducer(persistConfig, reducers);
let middlewares = [thunkMiddleware.default];
export default () => {
let store = createStore(persistedReducer, applyMiddleware(...middlewares));
let persistor = persistStore(store);
return { store, persistor };
};
But now, I'm getting the error TypeError: store.getState is not a function (In 'store.getState()', 'store.getState' is undefined).
Note: I've checked out many questions on stackoverflow with the same store.getState error, but they have very specific issues different from my setup.
Edit: Provider implementation (using RNNv2)
import { Navigation } from 'react-native-navigation';
import { Provider } from 'react-redux';
import store from '../../shared/redux/store';
import { registerScreens } from '../view/screens';
import { initialize } from './navigation';
/**
* Register screens and components for react native navigation
*/
registerScreens({ store, Provider });
const app = () => {
Navigation.events().registerAppLaunchedListener(() => {
initialize();
});
};
export default app;
Registerscreens:
const registerComponentWithRedux = (redux: any) => (
name: string,
component: any,
) => {
Navigation.registerComponentWithRedux(
name,
() => component,
redux.Provider,
redux.store,
);
};
export function registerScreens(redux: any) {
registerComponentWithRedux(redux)(screen, screen.default);
...
}
The issue is with the export, you are exporting a function that returns store in a key. So if you update the store reference in register screens it will work.
import returnStoreAndPersistor from '../../shared/redux/store';
const {store} = returnStoreAndPersistor();
registerScreens({ store, Provider });
If u use 'redux-persists' and return{store, persistor} from ../store just try THIS!!!!!
import returnStoreAndPersistor from "../../store";
const { store } = returnStoreAndPersistor()
console.log(store.getState())
For anyone who has this issue & is using Typescript where the only property on "getState()" is "_persist", the problem was for me caused by setting the type of your combined reducers to "any" due to the type error on supplying your store to persistStore:
Type AnyAction is not assignable to type {your action type}
When I fixed the above issue, the getState issue was resolved.
I fixed this by doing this:
const _ = (state: any = 0, _: AnyAction) => state;
const root = combineReducers({
_,
applicationStatusReducer,
...
});
It seems to resolve the issue (albeit in a hacky / not actually resolving the underlying issue kind of way), if you just add an empty reducer with the action type of "AnyAction" to it.
This issue is happening because this export default method instead of doing this you
can do it like this.
export let store = createStore(persistedReducer, applyMiddleware(...middleware));
export let persistor = persistStore(store);
sorry for any inconvenience
const store = createStore(persistedReducer, applyMiddleware(...middlewares));
const persistor = persistStore(store);
export { store, persistor };
and then
import { store, persistor } from '../../shared/redux/store';
I am trying to get React app to work properly with React Router and Redux, but I constantly getting TypeError: Cannot read property 'location' of undefined to any components that have redux. I am using connected-react-router lib to register router in the store but apparently I did something wrong
store/configureStore.js
import { createStore, applyMiddleware, compose } from 'redux';
import reducer from '../reducers';
import { persistStore } from 'redux-persist';
import { createBrowserHistory } from 'history';
import { routerMiddleware } from "connected-react-router";
let _persistor;
let _store;
export const history = createBrowserHistory();
export const getStore = (props) => {
if (_store) {
return _store;
}
const initialState = (props) ? {...props} : {};
_store = createStore(
reducer,
initialState,
compose(
applyMiddleware(routerMiddleware(history)),
window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__()
),
);
_persistor = persistStore(_store);
return _store;
};
export const getPersistor = () => {
return _persistor;
};
reducers/index.js
import { combineReducers } from 'redux';
import { persistReducer } from 'redux-persist';
import Menu from './Menu';
import SelectedServices from './SelectedServices';
import { connectRouter } from 'connected-react-router'
import { history } from "../store/configureStore";
const rootReducer = (history) => combineReducers({
router: connectRouter(history),
menu: Menu,
selectedServices: SelectedServices,
});
const reducer = persistReducer(
{
key: 'root',
storage: require('localforage'),
whitelist: [],
},
rootReducer(history),
);
export default reducer;
After three hours of debugging and reading more about each library of connected-react-router, react-redux, and react-router-dom I found one line which I skip several times, but eventually I checked it in my package.json. At current moment:
v6.0.0 requires React v16.4.0 and React Redux v6.0. (It does not support React Redux v7.0 yet. We're looking into the compability issue.)
I was using react-redux: v7.0.1 which a real facepalm to my self. So I downgraded to v6 and everything start works fine.
I have a problem running react-native app. I've got a store that uses redux-persist and some other middleware and the problem is when the app loads, data from the store is not there, only the default values. However as soon as I touch any button the data is there, so the problem is I can't delay rendering until the store is rehydrated. I've tried options with wrapping the app with PersistGate with both loading and bootstrapped values, however this does not work for me. The current code is:
Store config:
import { createStore, applyMiddleware, compose } from 'redux';
import promise from 'redux-promise-middleware';
import thunk from 'redux-thunk';
import { persistStore, persistReducer } from 'redux-persist';
import { AsyncStorage } from 'react-native';
import autoMergeLevel2 from 'redux-persist/lib/stateReconciler/autoMergeLevel2';
import rootReducer from './reducers';
const middleware = [
promise(),
thunk,
];
if (__DEV__) { // eslint-disable-line
// middlewares.push();
}
const enhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose; // eslint-disable-line
const persistConfig = {
key: 'root',
storage: AsyncStorage,
stateReconciler: autoMergeLevel2,
};
const pReducer = persistReducer(persistConfig, rootReducer);
export const store = createStore(pReducer, {}, enhancers(applyMiddleware(...middleware)));
export const persistor = persistStore(store);
Root Reducer:
import { combineReducers } from 'redux';
import loginScreenReducer from 'screens/login/LoginScreen.reducer';
export default combineReducers({
loginScreenReducer,
});
App:
import { AppLoading } from 'expo';
import React from 'react';
import { PersistGate } from 'redux-persist/integration/react';
import { Provider } from 'react-redux';
import Root from 'Root.component';
import { store, persistor } from 'reduxConfig/store';
export default class App extends React.Component {
state = { }
render() {
return (
<Provider store={store}>
<PersistGate persistor={persistor}>
{(bootstrapped) => {
if (bootstrapped) {
console.log(store.getState());
return <Root />;
}
return <AppLoading />;
}}
</PersistGate>
</Provider>
);
}
}
The component I'd like to make work correctly:
import React, { Component } from 'react';
import { connect } from 'react-redux';
import Navigator from './Navigator';
import { LoginScreen } from '../screens';
#connect(
state => ({
token: state.loginScreenReducer.token,
isLogged: state.loginScreenReducer.isLogged,
}),
)
export default class AppNavigator extends Component {
state = { }
render() {
const {
token,
} = this.props;
if (token) {
return <Navigator />;
}
return <LoginScreen />;
}
}
export const { router } = Navigator;
The component shows login screen first but if I touch any button on it, it renders Navigator component. Also when I get the state of the store (App, before returning <Root />, it has the next structure, containing empty object, which is currently default for loginScreenReducer:
The app works correctly except for redux-persist.
===== New Answer =====
The old answer is wrong, the problem came back later. It looks like it's a problem with redux-persist, or even expo, react-native-scripts or whatever, I was not been able to locate the problem source. The behavior I described before comes from redux-persist inability to access AsyncStorage. However the only thing that helps now is completely stopping expo and running it again, after that redux-persist is able to access AsyncStorage again.
It also looks like disabling live reload really helps with this problem if not stops it at all.
===== Old Answer =====
I've ended up by downgrading react-redux package to version 4.4.9, looks like this has solved the problem, otherwise the code I posted initially should be totally okay. The problem with auto-filling state on button press was caused my a couple of my own mistakes both from BE and FE sides, that lead to store be filled with different items. They are not in the given code. Also I've changed my store a little, I'm not sure if the initial problem was connected to enhancers combining. So now my store looks like this:
import { createStore, applyMiddleware } from 'redux';
import { persistStore, persistReducer } from 'redux-persist';
import autoMergeLevel2 from 'redux-persist/lib/stateReconciler/autoMergeLevel2';
import { AsyncStorage } from 'react-native';
import thunk from 'redux-thunk';
import promiseMiddleware from 'redux-promise-middleware';
import { createLogger } from 'redux-logger';
import { LOGOUT } from 'screens/login/LoginScreen.actions';
import appReducer from './reducers';
const middlewares = [
promiseMiddleware(),
thunk,
];
if (__DEV__) { // eslint-disable-line
middlewares.push(createLogger());
}
const rootReducer = (state, action) => {
if (action.type === LOGOUT) {
state = undefined;
}
return appReducer(state, action);
};
const persistConfig = {
key: 'root',
storage: AsyncStorage,
stateReconciler: autoMergeLevel2,
};
const persistedReducer = persistReducer(persistConfig, rootReducer);
export const store = createStore(persistedReducer, undefined, applyMiddleware(...middlewares));
export const persistor = persistStore(store);
I hope this will help somebody.
I am trying to get redux-persist working and cannot work out how to do it with the way my app is currently set up.
/**
* Create the store with dynamic reducers
*/
import { createStore, applyMiddleware, compose } from 'redux';
import { fromJS } from 'immutable';
import { routerMiddleware } from 'react-router-redux';
import createSagaMiddleware from 'redux-saga';
import thunk from 'redux-thunk';
import createReducer from './reducers';
import { persistStore, persistReducer } from 'redux-persist'
import storage from 'redux-persist/lib/storage'
const sagaMiddleware = createSagaMiddleware();
export default function configureStore(initialState = {}, history) {
// Create the store with two middlewares
// 1. sagaMiddleware: Makes redux-sagas work
// 2. routerMiddleware: Syncs the location/URL path to the state
const middlewares = [
sagaMiddleware,
thunk,
routerMiddleware(history),
];
const enhancers = [
applyMiddleware(...middlewares),
];
// If Redux DevTools Extension is installed use it, otherwise use Redux compose
/* eslint-disable no-underscore-dangle */
const composeEnhancers =
process.env.NODE_ENV !== 'production' &&
typeof window === 'object' &&
window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__
? window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__({
// TODO Try to remove when `react-router-redux` is out of beta, LOCATION_CHANGE should not be fired more than once after hot reloading
// Prevent recomputing reducers for `replaceReducer`
shouldHotReload: false,
})
: compose;
/* eslint-enable */
const store = createStore(
createReducer(),
fromJS(initialState),
composeEnhancers(...enhancers)
);
// Extensions
store.runSaga = sagaMiddleware.run;
store.injectedReducers = {}; // Reducer registry
store.injectedSagas = {}; // Saga registry
// Make reducers hot reloadable, see http://mxs.is/googmo
/* istanbul ignore next */
if (module.hot) {
module.hot.accept('./reducers', () => {
store.replaceReducer(createReducer(store.injectedReducers));
});
}
return store;
}
Where am I supposed to add redux-persist in here to make it connect to the store and save to localstorage?
I have tried a lot of different ways, all wrong, with a huge range of errors.. So no point posting all of them up.
Thanks in advance for helping :)
I'm not sure if this is of any use as I'm using redux-thunk and redux-localstorage (instead of redux-persist), but this works for me:
import { createStore, compose, applyMiddleware } from 'redux';
import rootReducer from '../reducers/rootReducer';
import thunk from 'redux-thunk';
import persistState from 'redux-localstorage';
const enhancer = compose(
applyMiddleware(thunk),
persistState(/*paths, config*/)
);
export default function configureStore() {
return createStore(
rootReducer,
window.__REDUX_DEVTOOLS_EXTENSION__ &&
window.__REDUX_DEVTOOLS_EXTENSION__(),
enhancer
);
}