Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

redux-persist: persist timed out for persist key "root" at eval #786

Open
lindslev opened this issue Apr 4, 2018 · 26 comments
Open

redux-persist: persist timed out for persist key "root" at eval #786

lindslev opened this issue Apr 4, 2018 · 26 comments

Comments

@lindslev
Copy link

lindslev commented Apr 4, 2018

screen shot 2018-04-04 at 4 41 45 am

^ In this screenshot, my config key is 'tf' because I've been trying to debug this error, lol. But yeah, I only see this error in the redux logs for the persist/REHYDRATE action. It doesn't seem to be affecting the actual desired behavior of redux-persist, and I can't figure out why it's being thrown and how to get rid of it. I dug through the source and looks like @rt2zz fixed something in February that's nearby but not exactly the same thing. This error seemed to pop up out of nowhere for us.

Here's my setup. I wrote most of this originally pre-v5, then migrated to 5.0.0-proto and have been on that version since just now. This error happens for me both on 5.0.0-proto and after upgrading to 5.9.1.

Looking at the migration guide now makes me think some things aren't quite right in my code, even if those things are not connected to this error. I'm using getStoredState & persistStore differently. Hopefully someone can give some insight 🙏 .

store.js

import { createStore, applyMiddleware } from 'redux';
import thunkMiddleware from 'redux-thunk';
import createLogger from 'redux-logger';
import { persistStore, persistReducer, getStoredState } from 'redux-persist';
import localforage from 'localforage';
import { toNumber } from 'lodash';

const middlewareList = [
 ....
];

if ( DEBUG ) {
  middlewareList.push(createLogger());
}

const config = {
  key: 'root',
  storage: localforage,
  debug: true,
  whitelist: ['activity', 'user']
};

const reducer = persistReducer(config, rootReducer);

const createStoreWithMiddleware = applyMiddleware(...middlewareList)(createStore);
const createRehydratedStore = (renderApp) => {
  getStoredState(config)
    .then((restoredState) => {
      const version = APP_VERSION; // eslint-disable-line no-undef
      const SCOPE_VERSION_LOCAL_STORAGE_KEY = 'scope-version';
      const savedVersion = window.localStorage.getItem(SCOPE_VERSION_LOCAL_STORAGE_KEY);
      const newVersion = toNumber(savedVersion) !== toNumber(version);

      const storeState = newVersion ? {} : restoredState;
      const store = createStoreWithMiddleware(reducer, storeState);
      const persistor = persistStore(store);

      if ( !savedVersion ) {
        window.localStorage.setItem(SCOPE_VERSION_LOCAL_STORAGE_KEY, version);
      } else if ( newVersion ) {
        persistor.purge();
        window.localStorage.setItem(SCOPE_VERSION_LOCAL_STORAGE_KEY, version);
      }
      renderApp(store, persistor);
    });
};

export default createRehydratedStore;

Note: we're trying to do some manual cache invalidation up there by passing an empty obj to createStore if there's a new app version. Maybe there's a better way to do this. I wrote it back on 5.0.0-proto, and looking at it again I'm not sure why we needed to both a) pass an empty obj and b) call persistor.purge.

app.js, which calls createRehydratedStore and gets the app rendered

import React from 'react';
import { render } from 'react-dom';
import { BrowserRouter } from 'react-router-dom';
import { Provider } from 'react-redux';
import { withRouter } from 'react-router';
import createStore from './store';
import { PersistGate } from 'redux-persist/integration/react';
import App from 'views/app';

const AppWithRouter = withRouter(App);

const renderApp = (store, persistor) => {
  render((
    <Provider store={store}>
      <PersistGate loading={null} persistor={persistor}>
        <BrowserRouter>
          <AppWithRouter />
        </BrowserRouter>
      </PersistGate>
    </Provider>
  ), document.getElementById('root'));
};

createStore(renderApp);
@sinkovsky
Copy link

Looks like I'm getting the same error, but it nothing is stored in the store.
Were you able to solve it?

@sinkovsky
Copy link

sinkovsky commented Apr 10, 2018

Upon further investigation looks like state is persisted only after timeout action is fired.
Then persisted state is updated with changes in redux, but after page reload it gets reset to initialState after timeout again.

Using react-boilerplate with switched off injected reducers and sagas and root Reducer state converted to simple object with Immutable in branches.

Setup is the same as in the documentation.

Could you please advise, @rt2zz ?

@lindslev
Copy link
Author

Update from me @sinkovsky - I noticed it's the size of my restoredState that I pass as a second argument to createStoreWithMiddleware that affects whether or not this timeout error is thrown.

@JanithaR
Copy link

JanithaR commented Apr 29, 2018

I ran into this issue a couple of hours ago. Now everything is perfectly fine. In the hopes of helping what happened in my case here it is.

I was working in the project and when ran outta battery in my mac so I just closed it and set aside yesterday. Today I just resumed working. And I remember the Android time mismatch warning was visible on my emulator. So lazy me just changed the clock setting and continued to work and that's when the issue popped up and I found this issue. I just ignored for the time being and kept working and when tired just shutdown my mac.

Now again I started working everything fresh and the issue is no longer there. So I guess just restart app, emulator, bundler or PC and it'll just go away.

@MaxInMoon
Copy link

I re-installed the app and the problem did not came back yet (for how long?)

@damathryx
Copy link

I'm having the same problem in Android

@xiongcaichang
Copy link

xiongcaichang commented Sep 14, 2018

I'm having the same problem on Android too

@luiscl32
Copy link

i'm having the same problem in android

@ozalexo
Copy link

ozalexo commented Sep 16, 2018

setTimeout is started in persistReducer method, but I did not found clearTimeout...
Here: 4360500#diff-78c77d9b1c28b6777a1c3ec40164bb64R83

So, I just disabled timeout in my configs

const persistConfig = {
  key: 'keyOfStore',
  storage: storage,
  // There is an issue in the source code of redux-persist (default setTimeout does not cleaning)
  timeout: null,
}
const appReducer = combineReducers({
  ...otherReducers,
  keyOfStore: persistReducer(persistConfig, keyOfStoreRedfucer),
})

@harakhovich
Copy link

We have the same issue with React Native and AsyncStorage and setting timeout to null helps.
Thanks @ozalexo

@UxmanKaxmi
Copy link

const persistConfig = {
key: 'keyOfStore',
storage: storage,
timeout: null,
}

timeout: null,
Did the trick. Thanks !

@WarrenBuffering
Copy link

Same here. Also chasing down an OOM bug, thinking the setTimeout could be responsible? Thoughts?

@AMhaish
Copy link

AMhaish commented Nov 22, 2019

I did everything and still getting the annoying error, any help please. My store as next:
`
import { createStore, applyMiddleware, compose } from 'redux';
import { persistStore, persistCombineReducers } from 'redux-persist';
import storage from 'redux-persist/lib/storage'; // default: localStorage if web, AsyncStorage if react-native
import thunk from 'redux-thunk';
import { createLogger } from 'redux-logger';
// import { composeWithDevTools } from 'redux-devtools-extension';
import reducers from '../reducers';
import Store from './initial';
import { Translations } from '../i18n';

// Redux Persist config
const config = {
key: 'root',
storage,
blacklist: ['system', 'auth', 'locale'],
debug: true
};

/const loggerMiddleware = createLogger({
level: 'info',
collapsed: true,
});
/
const logger = store => next => action => {
console.log('dispatching', action);
let result = next(action);
console.log('next state', store.getState().auth.isAuthenticated);
return result;
}

const crashReporter = store => next => action => {
try {
return next(action)
} catch (err) {
console.error('Caught an exception!', err)
Raven.captureException(err, {
extra: {
action,
state: store.getState()
}
})
throw err
}
}

const reducer = persistCombineReducers(config, reducers);

const middleware = [thunk, logger, crashReporter];

const configureStore = (core, with_initials, lang, companies) => {
if (with_initials) {
// Initializing the store
Store.system.companies = companies;
Store.system.companyFilter = core.getParamsManager().companyFilter;
if (core.getParamsManager().urlCompany)
Store.system.companyId = parseInt(core.getParamsManager().urlCompany);
Store.system.showHeader = core.getParamsManager().showHeader;
if (
Store.system.companyId !== undefined &&
companies !== undefined &&
companies[Store.system.companyId] !== undefined
) {
Store.system.company = companies[Store.system.companyId];
console.log(Store.system.company);
core.getParamsManager().setCompany(Store.system.company); // Important for now but will be removed in later versions
}
Store.system.core = core; // Storing the instance (it goes null in native by direct access)

switch (core.getParamsManager().lang) {
  case 'en':
    Store.locale.lang = 'en';
    Store.locale.obj = Translations.en;
    break;
  case 'tr':
    Store.locale.lang = 'tr';
    Store.locale.obj = Translations.tr;
    break;
  case 'de':
    Store.locale.lang = 'de';
    Store.locale.obj = Translations.de;
    break;
  case 'ar':
    Store.locale.lang = 'ar';
    Store.locale.obj = Translations.ar;
    break;
}

}
const store = createStore(
reducer,
Store,
compose(applyMiddleware(...middleware)),
);

const persistor = persistStore(
store,
null,
() => { store.getState(); },
);

return { persistor, store };
};

export default configureStore;

`

@lesmo
Copy link

lesmo commented Dec 20, 2019

I find this issue when setting an initialState in my reduce-reducers call:

import { createStore } from 'redux';
import { persistStore, persistReducer } from 'redux-persist';
import { composeWithDevTools } from 'redux-devtools-extension';
import reduceReducers from 'reduce-reducers';

const initialState = { whatever: true };
const rootReducer = reduceReducers(initialState, ...someMagic);
const persistedReducer = persistReducer({ key: 'root', storage }, rootReducer);
const store = createStore(persistedReducer, composeWithDevTools());
const persitor = persistStore(store);

Apparently, redux-persist is unable to rehydrate if a previous state is already set... for some reason. Without setting an initial state, it works without problems. Trying to get this to work for SSR, where server sends initialState for first render and the client does the rest.

@lesmo
Copy link

lesmo commented Dec 20, 2019

It's probably related to #189 or #226, but intriguingly in the client I was doing something like this:

// Here my ReduxService class sets up everything
// Like in my previous reply, window.__PRELOADED_STATE__ would be initialState
const reduxService = new ReduxService(window.__PRELOADED_STATE__);

After I did a spread like this, it started working as expected without the timeout issue.

const reduxService = new ReduxService({...window.__PRELOADED_STATE__});

It is however behaving like in #226, though at this point in the code no actions are being sent (except for the @@init). I've been testing it and, although persist/PERSIST action is dispatched before persist/REHYDRATE, it's working for me. Mind you, I've created my own stateReconciler so... perhaps it's not as simple as that.

@bvanderhoof
Copy link

I had to set the timeout property to 1. 0 and null didn't work. Is it really expected that every time you call persistStore for the first time, it times out because it can't find the root key?

@tamdao
Copy link

tamdao commented Oct 5, 2020

Can you take a look at this issue? It happened two years

@Moumene
Copy link

Moumene commented Dec 30, 2020

Still happening now too,
The timeout: null is just removing the crash from happening but I got some weird behavior in the app,
At some point i get an old state just after updating some data.

@iagormoraes
Copy link

this package is not being maintained anymore as said in this reply.

@andrewzey
Copy link

this package is not being maintained anymore as said in this reply.

Just to be clear, I'm not affiliated with this project in any way, I was just observing that this package is clearly abandoned given the lack of activity.

@sergeushenecz
Copy link

sergeushenecz commented Jul 22, 2022

Still happening now too, The timeout: null is just removing the crash from happening but I got some weird behavior in the app, At some point i get an old state just after updating some data.

How do you fix issue about old state after updating some data. @Moumene

@WarrenBuffering
Copy link

WarrenBuffering commented Jul 26, 2022

Just my 2 cents here, but writing your own persist via async-storage is really pretty easy. Especially if you're waiting on updates to an abandoned package. Encrypted storage is a pretty cool package too if you're dealing with sensitive info

@sergeushenecz
Copy link

Just my 2 cents here, but writing your own persist via async-storage is really pretty easy. Especially if you're waiting on updates to an abandoned package. Encrypted storage is a pretty cool package too if you're dealing with sensitive info

Maybe exist alternative packages do you know?

@mtnt
Copy link

mtnt commented Aug 12, 2022

I faced the issue if I passed initial state like {_persist: {version: -1, rehydrated: false}} only. But now I use initialState = {} as PersistedState and all is ok

@thoughtworks-tcaceres
Copy link

getting this same issue in 2024.
react-redux 9.1.2
@reduxjs/toolkit 2.2.3
redux-persist 6.0.0

@sanduluca
Copy link

sanduluca commented Nov 7, 2024

setTimeout is started in persistReducer method, but I did not found clearTimeout... Here: 4360500#diff-78c77d9b1c28b6777a1c3ec40164bb64R83

So, I just disabled timeout in my configs

const persistConfig = {
  key: 'keyOfStore',
  storage: storage,
  // There is an issue in the source code of redux-persist (default setTimeout does not cleaning)
  timeout: null,
}
const appReducer = combineReducers({
  ...otherReducers,
  keyOfStore: persistReducer(persistConfig, keyOfStoreRedfucer),
})

You are right that there is not clearTimeout, but it doesn't mean that this is the problem here. Even with a clearTimeout the problem would be the same.
The author is using a variable outside of setTimeout to know if the state was not rehydrated.

https://github.com/rt2zz/redux-persist/blob/master/src/persistReducer.ts#L72-L101

The problem here is that _sealed is not changed to true because a error is thrown in action.rehydrate(config.key, payload, err); and this is already your problem.

Changing the order here would fix the issue with timed out persist and would reveal the probleme in the developers code. I would also add a try catch block to console.error the error
https://github.com/rt2zz/redux-persist/blob/master/src/persistReducer.ts#L86-L87

Here is a patch-package

diff --git a/node_modules/redux-persist/lib/persistReducer.js b/node_modules/redux-persist/lib/persistReducer.js
index 1116881..9ce961b 100644
--- a/node_modules/redux-persist/lib/persistReducer.js
+++ b/node_modules/redux-persist/lib/persistReducer.js
@@ -70,8 +70,12 @@ function persistReducer(config, baseReducer) {
         if (process.env.NODE_ENV !== 'production' && _sealed) console.error("redux-persist: rehydrate for \"".concat(config.key, "\" called after timeout."), payload, err); // only rehydrate if we are not already sealed
 
         if (!_sealed) {
-          action.rehydrate(config.key, payload, err);
           _sealed = true;
+          try {
+            action.rehydrate(config.key, payload, err);
+          } catch (error) {
+            console.error("redux-persist: rehydrate for \"".concat(config.key, "\" failed"), error);
+          }
         }
       };
 

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests