rt2zz / redux-persist

persist and rehydrate a redux store
MIT License
12.97k stars 867 forks source link

store is rehydrated twice #909

Open lucasriondel opened 6 years ago

lucasriondel commented 6 years ago

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 ?

sregg commented 6 years ago

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

evertbouw commented 6 years ago

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.

lucasriondel commented 6 years ago

@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
})
tonmanayo commented 6 years ago

Also getting rehydrate twice, different setup

nero2009 commented 5 years ago

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

lucasriondel commented 5 years ago

@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.

ammoradi commented 5 years ago

any news about this?

nero2009 commented 5 years ago

@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.

ammoradi commented 5 years ago

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

nero2009 commented 5 years ago

can i see your store and reducer?

ammoradi commented 5 years ago

@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 };
}
nero2009 commented 5 years ago

@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

elbaumpj commented 5 years ago

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.

hanchiang commented 5 years ago

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.

Sushant-Sardeshpande commented 5 years ago

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.

sureshbakshi commented 4 years ago

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

zvs001 commented 1 year ago

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.