How to set up redux-persist with thunk, middleware and immutable JS? - reactjs

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
);
}

Related

React Redux Persist : How to control when persist data in indexeddb

I'm working on a React/redux application using redux-persist and I have a huge amount of data to persist, I want to be able to persist my Redux Store ONLY when I click on a button, otherwise I don't want to store my data.
This is my store :
import {
legacy_createStore as createStore,
combineReducers,
applyMiddleware
} from "redux";
import { composeWithDevTools } from "redux-devtools-extension/developmentOnly";
import { persistStore, persistReducer } from "redux-persist";
import storage from "redux-persist-indexeddb-storage";
import thunk from "redux-thunk";
import dataReducer from "./reducers/dataReducer";
const reducer = combineReducers({
data: dataReducer
});
const persistConfig = {
key: "STORE",
storage: storage("STORE")
};
const persistedReducer = persistReducer(persistConfig, reducer);
const initialState = {};
const middleware = [thunk];
const composeEnhancers = composeWithDevTools({
features: {
dispatch: true // dispatch custom actions or action creators
}
});
const store = createStore(
persistedReducer,
initialState,
composeEnhancers(
applyMiddleware(...middleware)
// other store enhancers if any
)
);
const persistor = persistStore(store);
export { persistor };
export default store;
My Index.js look like this :
And I've create a component to show my data :
By doing that : the redux persist data starting from the first render (this is the indexedDb)
What I'm looking for is to set my indexedDB only when I click on the button and trigger the persistData function.
This is my code , So if you have an idea how to achieve this please.

Clear the redux store after logout

I have created a redux store below, the only thing I want to clear the redux store when user logout from the application, so store having the latest and updated data of currently logged user.
import { createStore, applyMiddleware, compose } from 'redux';
import { persistStore, persistReducer } from 'redux-persist';
import storage from 'redux-persist/lib/storage';
import rootReducer from './Reducers';
import { sessionService } from 'redux-react-session';
import thunk from 'redux-thunk';
const persistedReducer = persistReducer({ key: 'root', storage }, rootReducer);
const composeEnhancers =
(typeof window !== 'undefined' &&
window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__) ||
compose;
export const store = createStore(
persistedReducer,
composeEnhancers(
applyMiddleware(thunk)
)
);
export const persistor = persistStore(store);
sessionService.initSessionService(store);
The above snippet I wrote to create the store using redux persist
please suggest

action.rehydrate is not a function

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));

React Boilerplate with Redux Dev Tools Error?

I'm using React Boilerplate which uses Redux. I have downloaded the Redux Chrome Dev Tools and I keep getting this error
combineReducers.js:29 The previous state received by the reducer is of unexpected type. Expected argument to be an instance of Immutable.Iterable with the following properties: "route", "language", "global".
I have been debugging this thing through and through to no avail. What I've seen is that combineReducers returns a function and the first line of it is
var inputState = arguments.length <= 0 || arguments[0] === undefined ? _immutable2.default.Map() : arguments[0];
I noticed that a breakpoint here hits twice. The first time, the arguments are exactly what they should be
But the second time my arguments look like this
For some reason, arguments[0] changes from a Map type to a literal object. So two questions:
Why does Redux run combineReducers multiple times on INIT?
Why would my arguments change when initializing?
Here's the code from store.js in React Boilerplate. I only added the import and call for persistStore.
import { createStore, applyMiddleware, compose } from 'redux';
import { fromJS } from 'immutable';
import { routerMiddleware } from 'react-router-redux';
import createSagaMiddleware from 'redux-saga';
import createReducer from './reducers';
import { persistStore, autoRehydrate } from 'redux-persist';
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,
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__ : compose;
/* eslint-enable */
//get the state from localStorage
// const persistedState = loadState();
const store = createStore(
createReducer(),
fromJS(initialState),
composeEnhancers(...enhancers)
);
// Extensions
store.runSaga = sagaMiddleware.run;
store.asyncReducers = {}; // Async reducer registry
// Make reducers hot reloadable, see http://mxs.is/googmo
/* istanbul ignore next */
if (module.hot) {
module.hot.accept('./reducers', () => {
import('./reducers').then((reducerModule) => {
const createReducers = reducerModule.default;
const nextReducers = createReducers(store.asyncReducers);
store.replaceReducer(nextReducers);
});
});
}
persistStore(store);
return store;
}
EDIT
Here is the main reducer file. It brings in other reducer files.
/**
* Combine all reducers in this file and export the combined reducers.
* If we were to do this in store.js, reducers wouldn't be hot reloadable.
*/
import { combineReducers } from 'redux-immutable';
import { fromJS } from 'immutable';
import { LOCATION_CHANGE } from 'react-router-redux';
import languageProviderReducer from 'containers/LanguageProvider/reducer';
import globalReducer from 'containers/App/reducer';
/*
* routeReducer
*
* The reducer merges route location changes into our immutable state.
* The change is necessitated by moving to react-router-redux#4
*
*/
// Initial routing state
const routeInitialState = fromJS({
locationBeforeTransitions: null,
});
/**
* Merge route into the global application state
*/
function routeReducer(state = routeInitialState, action) {
switch (action.type) {
/* istanbul ignore next */
case LOCATION_CHANGE:
return state.merge({
locationBeforeTransitions: action.payload,
});
default:
return state;
}
}
/**
* Creates the main reducer with the asynchronously loaded ones
*/
export default function createReducer(asyncReducers) {
return combineReducers({
route: routeReducer,
language: languageProviderReducer,
global: globalReducer,
...asyncReducers,
});
}

Uncaught Error: Expected the enhancer to be a function

I am trying to call the reducer from the component and want to render that in component , but when I am trying to store the reducer in the createStore() method of redux above error is coming. My Code is like this:-
import { applyMiddleware, compose, createStore } from 'redux'
import thunk from 'redux-thunk'
import { browserHistory } from 'react-router'
import makeRootReducer from './reducers'
import { updateLocation } from './location'
import allReducers from './reducers';
export default (initialState = {}) => {
// ======================================================
// Middleware Configuration
// ======================================================
const middleware = [thunk]
// ======================================================
// Store Enhancers
// ======================================================
const enhancers = []
let composeEnhancers = compose
if (__DEV__) {
const composeWithDevToolsExtension = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__
if (typeof composeWithDevToolsExtension === 'function') {
composeEnhancers = composeWithDevToolsExtension
}
}
// ======================================================
// Store Instantiation and HMR Setup
// ======================================================
const store = createStore(
allReducers,
makeRootReducer(),
initialState,
composeEnhancers(
applyMiddleware(...middleware),
...enhancers
)
)
I am getting error :Uncaught Error: Expected the enhancer to be a
function
You are passing in two reducers to the createStore function instead of one.
Since the third argument to createStore is always the enhancer function it thinks that the 'initiaeState' variable is an enhancer function since you are passing this in as the thid argument to createStore.
createStore expects to receive the following arguments:
reducer (Function): A reducing function that returns the next state
tree, given the current state tree and an action to handle.
[preloadedState] (any): The initial state. You may optionally
specify it to hydrate the state from the server in universal apps,
or to restore a previously serialized user session. If you produced
reducer with combineReducers, this must be a plain object with the
same shape as the keys passed to it. Otherwise, you are free to pass
anything that your reducer can understand.
[enhancer] (Function): The store enhancer. You may optionally
specify it to enhance the store with third-party capabilities such
as middleware, time travel, persistence, etc. The only store
enhancer that ships with Redux is applyMiddleware().
Remember the root reducer in your app should combine all your reducers into one single reducer.
From the Redux docs
First and foremost, it's important to understand that your entire
application really only has one single reducer function
I encountered a similar error TypeError: enhancer(...) is not a function because I was passing a function instead of a plain object as the initial state.
Ie my error was due to missing brackets on my initial state generator function:
const store = createStore(
reducer,
testState, // <<< missing brackets!! doh
applyMiddleware(sagaMiddleware)
)
When it should have been:
const store = createStore(
reducer,
testState(),
applyMiddleware(sagaMiddleware)
)
In my case it was a wrong import.
❌ Wrong import:
import { applyMiddleware, createStore } from "redux";
import rootReducer from "../reducers/index";
import createSagaMiddleware from "redux-saga";
import rootSaga from "redux/sagas";
import { compose } from "#material-ui/system"; // <= ERROR: wrong compose
const sagaMiddleware = createSagaMiddleware();
const composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose;
const store = createStore(
rootReducer,
{},
composeEnhancers(applyMiddleware(sagaMiddleware))
);
sagaMiddleware.run(rootSaga);
export default store;
✅ Correct import:
import { applyMiddleware, compose, createStore } from "redux"; // <= Correct!
import createSagaMiddleware from "redux-saga";
import rootSaga from "redux/sagas";
import rootReducer from "../reducers/index";
const sagaMiddleware = createSagaMiddleware();
const composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose;
const store = createStore(
rootReducer,
{},
composeEnhancers(applyMiddleware(sagaMiddleware))
);
sagaMiddleware.run(rootSaga);
export default store;

Resources