redux-persist icon indicating copy to clipboard operation
redux-persist copied to clipboard

store is rehydrated twice

Open lucasriondel opened this issue 6 years ago • 17 comments

I know it's important to stay hydrated but my store is being hydrated twice here :

store.ts

const persistConfig = {
  key: 'root',
  storage,
  whitelist: ['users']
}

const usersPersistConfig = {
  key: 'users',
  storage,
  whitelist: ['availableUsers']
}

const persistedReducer = persistReducer(
  persistConfig,
  combineReducers({
    nav,
    users: persistReducer(usersPersistConfig, users),
    patient,
    error,
    survey
  })
)

const store = createStore(persistedReducer, applyMiddleware(navMiddleware, thunk, logger))

export const persistor = persistStore(store)

export default store

app.tsx

import store, { AppWithNavigationState, persistor } from './src/redux/store'
import { PersistGate } from 'redux-persist/integration/react'

export default class App extends React.Component {
  render() {
    return (
      <Provider store={store}>
        <PersistGate loading={null} persistor={persistor}>
          <AppWithNavigationState />
        </PersistGate>
      </Provider>
    )
  }
}

at startup: image

I may do something wrong since the first hydration bypasses whitelist rules.

Any idea ?

lucasriondel avatar Sep 18 '18 15:09 lucasriondel

I'm seeing the same thing with the same setup. I don't think we're doing anything wrong though.

sregg avatar Sep 26 '18 21:09 sregg

I ran into this as well, you have to blacklist users from root, not whitelist. In this example you can even remove the entire root persistor.

evertbouw avatar Sep 28 '18 07:09 evertbouw

@evertbouw @sregg i only want availableUsers to be rehydrated, not the other properties.

as you've said @evertbouw , i have removed the root persistor, and i've changed it to :

const usersPersistConfig = {
  key: 'users',
  storage,
  whitelist: ['availableUsers']
}

export default combineReducers({
  users: persistReducer(usersPersistConfig, users),
  error,
  patient,
  survey,
  score
})

lucasriondel avatar Sep 28 '18 14:09 lucasriondel

Also getting rehydrate twice, different setup

tonmanayo avatar Oct 04 '18 16:10 tonmanayo

Hi @tonmanayo @lucasriondel did you find a solution for this, I am currently struggling with this

nero2009 avatar Mar 29 '19 14:03 nero2009

@nero2009 hi ! won't be able to help, sorry, but i managed to fixed it but i don't remember how... I'm working on a new project now.

lucasriondel avatar Mar 29 '19 15:03 lucasriondel

any news about this?

ammoradi avatar Apr 04 '19 07:04 ammoradi

@ammoradi in my case, apparently I blacklisted the reducers I didn't want to persist to Localforage twice(in my root reducer and store) that was what triggerred the persist/REHYDRATE action twice.

nero2009 avatar Apr 04 '19 08:04 nero2009

@nero2009 In my case I have this issue even without black/whitelist! and both persist/PERSIST and persist/REHYRATE called twice

ammoradi avatar Apr 04 '19 08:04 ammoradi

can i see your store and reducer?

nero2009 avatar Apr 04 '19 14:04 nero2009

@nero2009

import { applyMiddleware, compose, createStore } from 'redux';
import thunk from 'redux-thunk';
import { persistStore, persistReducer } from 'redux-persist';
import storage from 'redux-persist/es/storage';
import RootReducer, { SaveSubsetFilter } from '@redux/index';

const middlewares = [
	thunk
];

let composer;
if (GLOBAL.__DEV__) {
	GLOBAL.XMLHttpRequest = GLOBAL.originalXMLHttpRequest || GLOBAL.XMLHttpRequest;
	const { composeWithDevTools } = require('redux-devtools-extension');
	composer = composeWithDevTools;
	middlewares.push(require('redux-immutable-state-invariant').default());
}
else {
	composer = compose;
	console.log = () => {};
	console.warn = () => {};
	console.error = () => {};
}

const config = {
	timeout: 12000,
	key: 'root',
	storage,
	whitelist: ['core'],
	debug: false,
	transforms: SaveSubsetFilter
};

const enhancer = composer(
	applyMiddleware(...middlewares)
);

const reducer = persistReducer(config, RootReducer);
const store = createStore(reducer, enhancer);
const persistor = persistStore(store);

export default function configureStore() {
	if (module.hot) {
		module.hot.accept(() => {
			const nextRootReducer = require('@redux/index').default;
			const nextReducer = persistReducer(config, nextRootReducer);
			store.replaceReducer(nextReducer);
		});
	}
	return { persistor, store };
}

ammoradi avatar Apr 06 '19 08:04 ammoradi

@ammoradi from your store.js, I don't know exactly what might be causing it but I think you should try probing your configureStore function

nero2009 avatar Apr 08 '19 06:04 nero2009

Not sure if this could be helpful for anyone, but I was having a similar problem, except the second hydration was breaking my app by updating a nested persist with an undefined payload. I also needed the second hydration because of the nested persist. I realized I needed to blacklist that property on the root config, and not whitelist it on the nested config. Here's an example:

nested persist for auth (I needed this due to the hardSet state reconciliation):

const authConfig = {
  key: 'auth',
  storage,
  stateReconciler: hardSet,
}

export default authReducer(authConfig, auth)

rootReducer:

export default combineReducers({
  authReducer,
  form: FormReducer,
  user,
})

root persist:

const persistConfig = {
  key: 'root',
  blacklist: ['auth'],
  storage,
}
const persistedReducer = persistReducer(persistConfig, rootReducer)
const store = createStore(persistedReducer, initialState, enhancer)

I know this might not be completely aligned with the original issue, but I came across this issue in the search to resolve my bug, so I'm leaving it here in case it could be helpful to anyone else.

elbaumpj avatar May 30 '19 14:05 elbaumpj

I ran into this as well, you have to blacklist users from root, not whitelist. In this example you can even remove the entire root persistor.

This is the solution. I think others, like me, have misunderstood the correct way to persist certain keys of a nested reducer state. We have to blacklist the nested reducer state first, then whitelist the individual keys we want to persist.

hanchiang avatar Jun 25 '19 14:06 hanchiang

For me this happened where in the onComplete callback of the persistStore I was updating the state of the component which parented the PersistGate. Shifting the state related logic to the root within the PersistGate instead solved this for me.

Sushant-Sardeshpande avatar Sep 18 '19 05:09 Sushant-Sardeshpande

store is rehydrated twice

HERE IS MY STORE FILE:

`import { routerMiddleware } from 'connected-react-router'
import { applyMiddleware, compose, createStore } from 'redux';
import { createLogger } from 'redux-logger';
import createSagaMiddlware from 'redux-saga';
import createRootReducer from '../state/reducers';
import { persistReducer } from 'redux-persist';
import storage from 'redux-persist/lib/storage';
import { createBrowserHistory } from "history";
const history = createBrowserHistory();


const persistConfig = {
    key: 'root',
    storage,
}
const sagaMiddleware = createSagaMiddlware();
const composeEnhancers = (window as any).__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose;

const getAppliedMiddleware = (routerhistory: any) => {
    if (process.env.NODE_ENV === 'development') {
        return applyMiddleware(
            sagaMiddleware,
            routerMiddleware(routerhistory),
            createLogger(),
        );
    }
    return applyMiddleware(
        sagaMiddleware,
        routerMiddleware(routerhistory),
        createLogger(),
    )
}


export const STORE = createStore(
        persistReducer(persistConfig, createRootReducer(history)),
        {},
        composeEnhancers(
            getAppliedMiddleware(history),
        ),
    );


`

HERE IS MY APP.TSX FILE:

`
import "./styles/base.scss";
import { ConnectedRouter } from "connected-react-router";
import { createBrowserHistory } from "history";
import * as React from "react";
import * as ReactDOM from "react-dom";
import { Provider } from "react-redux";
import { Route as Router, Switch } from "react-router";
import { Route } from "react-router-dom";
const history = createBrowserHistory();
import {STORE} from "./store/index";
import { PersistGate } from "redux-persist/integration/react";
import { persistStore } from "redux-persist";
import HeaderComponent from "./components/home/header_component";
import { AppRoutes } from "./utils/routes";


ReactDOM.render(
  <Provider store={STORE}>
    <PersistGate loading={null} persistor={persistStore(STORE)}>
      <ConnectedRouter history={history}>
        <Router>
            <HeaderComponent  />
            <div className="page-wrapper">
              <Switch>
                {AppRoutes.map((route: any) => {
                  return <Route key={route.path} {...route} />;
                })}
              </Switch>
            </div>
        </Router>
      </ConnectedRouter>
    </PersistGate>
  </Provider>,
  document.getElementById("app")
);
`

ISSUE I AM FACING: Same data rehydrating twice:

Screenshot 2020-04-30 at 8 10 22 PM

Screenshot 2020-04-30 at 8 12 29 PM

sureshbakshi avatar Apr 30 '20 14:04 sureshbakshi

It can happen when there are 2 instances of redux-persist.

I had issue when I have 1 bundle connected and second, which replaces the first one. In result only 1 app exists, but by fact that there are 2 same bundles created this issue. I noticed that first rehydrate had previous config, which conflicted with current one.

zvs001 avatar Aug 30 '23 13:08 zvs001