Open jln-dk opened 6 years ago
+1 !
We're actually having the exact same problem in my company. We are just using AsyncStorage though. @jesperlndk did you find a solution? Or a different way to store the data?
I've also noticed sometimes we get the following error:
{
err: Error: redux-persist: persist timed out for persist key "myKey" at blob:http://localhost:8081/d0182199-b2a0-4806-ad16-47af460661d8:18512:45,
key: "myKey"
payload: undefined
type: "persist/REHYDRATE"
}
Same here. I started looking into it and I think what's happening is the getItem()
Promise on app load rejects with an error. That error is passed to the REHYDRATE
action. The action does not consider the error and just writes undefined
to the store, causing the setItem
middleware to run (because it detected a "change") and "erasing" the data.
I am currently looking for issues with a suggested fix or a PR. Might make my own PR but I'm not sure what correct behavior should be. Probably just to not write anything to local store if getItem
failed. And I might need to add a new error handler to the config so consumer can handle when a rehydration error occurs.
@jrmurad I moved from AsyncStorage to FSStorage (based on react-native-fs) and the issue seem to appear way less often, but it still occurs from time to time on some Android devices. An appropriate fix would be to have a way to detect and display they error and stop the rehydrate process.
Same issue, any updates?
I can confirm that this still happens when using FSStorage. It happens of both v5 & v4. Seems to have something to do with the autoRehydrate
which is not getting logged and data not persisted in some cases.
After more testing it seems to be working with redux-persist-filesystem-storage without erasing data randomly. It could be something to do with AsyncStorage. @lilosir
Anyone found a solution @jrmurad @jesperlndk @alexmngn?
@alanlanglois Using redux-persist-filesystem-storage worked for me. Using in production.
@lukebrandonfarrell Few mounths ago I gave it a try, unfortunatly it wasn't working with an expo detached project. (rn-fetch-blob wasn't at least). I then changed a little the lib to use react-native-fs instead of rn-fetch-blob. It fixed the problem in most cases, but I still have the issue from time to time (way less often). Do you have a large audience are you sure it totally fixed your problem? (I got like 10K+ install on Android)
I was able to reproduce the issue by setting timeout
to something incredibly low like 10ms. The store was consistently overwritten by initialState
. That's when I tested a few weeks ago, and I didn't write a test. I'd like to write a test to reproduce it, but I wanted to mention that in case anyone else is able to investigate further.
timeout
is a key in the persist config, but it is not yet documented in the README.
@ssorallen It seems to be talked here: https://github.com/rt2zz/redux-persist/issues/717
redux-persist's timeout
functionality definitely wipes out the store and rehydrates it with initial state. I don't know if that's the only issue here, but I have confirmed locally and in my own app that it does wipe out my store.
You can disable the timeout functionality entirely by passing timeout: 0
in your persistConfig. If your store takes a while to rehydrate then the PersistGate
will continue to show the loading
state. If your store never rehydrates then the PersistGate will never go away. However, it won't ever hit the timeout
functionality and wipe your store. Your users can refresh the app or whatever it takes in case that slow rehydration was transient.
In your persist config, prevent the timeout setup (persistReducer.js#L88-99) (the default timeout
value is 5000):
const persistConfig: {
...
timeout: 0, // The code base checks for falsy, so 0 disables
};
A complete solution for redux-persist would be to add a timeout
component to PersistGate
that would render if your timeout is hit and stop the rehydration work. What it shouldn't do is then rehydrate your store with initialState, which is the current functionality.
Happening on my app as well! Some of my users have reported to me that all their journal data disappeared. Have not been able to reproduce.
"react": "16.8.3",
"react-native": "0.59.8",
"react-redux": "^6.0.0",
"redux": "^4.0.1",
"redux-persist": "^5.10.0",
"redux-thunk": "^2.3.0",
I've been working on an Expo-based React Native app that encountered the same problem.
After trying the timeout fix mentioned above and finding that didn't work for us, we ended up using redux-persist-expo-fs-storage
and haven't been experiencing the same problems since.
If you're using Expo, I'd recommend trying that out as your storage solution. Just for reference, here's some more package.json info:
"react": "16.5.0",
"react-native": "https://github.com/expo/react-native/archive/sdk-32.0.0.tar.gz",
"react-redux": "^5.1.0",
"redux": "^4.0.1",
"redux-persist": "^5.10.0",
"redux-persist-expo-fs-storage": "^1.2.2",
"redux-thunk": "^2.3.0",
This bug is also happening in our production app. We have a lot of users complaining because their data (stored using the library: redux-persist-fs-storage), is being sporadically erased... We tried adjusting the timeout config, but that didn't solve the problem. We are not using Expo. If anyone solved this issue, please help me with the solution. Many thanks!!!!
We're experiencing the same issue as well now (for a couple of months). I can give this information (which is about the same as the OP)
persistStore
function when rehydrating. The longest execution time we had was 887ms (in production), so it's not hitting the timeout at all. Pretty sure that can be ruled out.If there's any way we can help out (by providing logs, setups, etc) please contact me. It's becoming a big problem for us and our users.
Is this repo maintained any more? These issues date back a full two years now, and the last commit to master was September 2019.
If this is affecting your production application and causing data loss, it might be time to find a replacement for redux-persist.
Well, I think why people are still posting all the issues here is because when you're using redux, there doesn't seem to be a lot of alternative to redux-persist.
In my case explicitly setting the whitelisted reducers, as well as timeout to 0 in redux persist config actually helped. No more store data loss on app restart after that.
I think by now, I've tried every possible combination of workarounds. They always get persisted and rehydrated except on app update. We implemented our own solution now to persist a specific reducer which contained data that never gets synced with the server.
Btw this issue drove us to build our own persist library (a year ago): https://github.com/aspect-apps/redux-persist-machine
As we got frustrated with little issues like this, we wanted a much simpler implementation, with more customisation over the data we load, and how we load it.
Does anyone have an update on this? This is actually making us lose customers.
Any fix on this issue?
@wmonecke @ArchanaNair If redux-persist is making you lose customers, Iβd likely abandon this library. It has not been maintained in several years at this point. (see dates on issues and comments)
You might have more success with major version 5, but if these are decisions that impact your revenue then this library does not seem reliable enough.
I managed to identify and solve the same issue on Android using redux persist with async storage. Here is what the problem was in my case: As @jrmurad mentioned the error manifests itself on getItem. To identify it I patched the persistReducer.js as follows and threw an error instead of rehydrating with undefined:
diff --git a/node_modules/redux-persist/lib/persistReducer.js b/node_modules/redux-persist/lib/persistReducer.js
index 1116881..6aed0e7 100644
--- a/node_modules/redux-persist/lib/persistReducer.js
+++ b/node_modules/redux-persist/lib/persistReducer.js
@@ -94,6 +94,7 @@ function persistReducer(config, baseReducer) {
if (typeof action.rehydrate !== 'function' || typeof action.register !== 'function') throw new Error('redux-persist: either rehydrate or register is not a function on the PERSIST action. This can happen if the action is being replayed. This is an unexplored use case, please open an issue and we will figure out a resolution.');
action.register(config.key);
getStoredState(config).then(function (restoredState) {
+ console.log('redux persist reducer')
var migrate = config.migrate || function (s, v) {
return Promise.resolve(s);
};
@@ -102,11 +103,14 @@ function persistReducer(config, baseReducer) {
_rehydrate(migratedState);
}, function (migrateErr) {
if (process.env.NODE_ENV !== 'production' && migrateErr) console.error('redux-persist: migration error', migrateErr);
-
- _rehydrate(undefined, migrateErr);
+ console.log('redux persist migration error', migrateErr)
+ throw migrateErr
+ // _rehydrate(undefined, migrateErr);
});
}, function (err) {
- _rehydrate(undefined, err);
+ console.log('redux persist error', err)
+ throw err
+ // _rehydrate(undefined, err);
});
return _objectSpread({}, baseReducer(restState, action), {
_persist: {
In my case this yielded an error sometime later (due to this error being not very repeatable) as follows:
redux persist error [Error: Row too big to fit into CursorWindow requiredPos=0, totalRows=1]
This lead me to the local SQLite issue of trying to read big text entries for single rows exceeding the default of 4mb for a single row, and that indeed was the issue. A few thousand rows of text were being stored in the redux store under a single row.
Saving this as a JSON file and storing a reference to that file in redux solved the issue. You could expand that limit but I figured it's probably there for good reason. I hope this saves someone else.
Shared credit to @Federkun
@ssorallen I have been searching for a replacement with no success!
I am getting this issue where i store the userType in the redux-persist but when ever the app is updated it loses all its data and the app becomes un useable the user have to log out in order to store the userType from the api i have been looking for hours but still was'nt able to find a solution
We are still facing this, seems like this repo is not actively maintained
I think by now, I've tried every possible combination of workarounds. They always get persisted and rehydrated except on app update. We implemented our own solution now to persist a specific reducer which contained data that never gets synced with the server.
Would you mind sharing what did you do?
I am getting this issue where i store the userType in the redux-persist but when ever the app is updated it loses all its data and the app becomes un useable the user have to log out in order to store the userType from the api i have been looking for hours but still was'nt able to find a solution
What platform does that happen on? We too are facing the same issue on iOS, but that doesn't happen 100% of time (which compicates things)
We're using redux-persist-filesystem-storage
, thus it could be related to #https://github.com/robwalkerco/redux-persist-filesystem-storage/issues/44
Fixing this is rather hard since its almost impossible to reproduce due its random nature.
For anyone experiencing data loss after app update on iOS, this is probably not the redux-persist issue, but of a storage engine, or its filesystem interaction logic, to be exact.
That my happen if a storage engine keeps persisted state in a document directory, path to which may look something like this
data/Containers/Data/Application/APP_UUID/Documents/
, where APP_UUID
is a unique identifier which changes on app updates. I suggest, after an app update, storage engine, for some reason, tries to look for persisted state in an old directory, finds nothing, which in return causes redux-persist
to revert the store to its default state.
Why does that happen is a mystery, since this error is not 100% reproducible.
In the end, that is just my suggestion, which may not be the case.
For anyone experiencing data loss after app update on iOS, this is probably not the redux-persist issue, but of a storage engine, or its filesystem interaction logic, to be exact.
That my happen if a storage engine keeps persisted state in a document directory, path to which may look something like this
data/Containers/Data/Application/APP_UUID/Documents/
, whereAPP_UUID
is a unique identifier which changes on app updates. I suggest, after an app update, storage engine, for some reason, tries to look for persisted state in an old directory, finds nothing, which in return causesredux-persist
to revert the store to its default state.Why does that happen is a mystery, since this error is not 100% reproducible.
In the end, that is just my suggestion, which may not be the case.
You are right, the APP_UUID
may be changed after app update, but shouldn't the store engine save and query data from a relative path? APP_UUID
may be changed, but the Document
folder is always there. This should not be the case since same issue also happens on Android. This issue happens on production apps and it's still not resolved after 3 years. Looks like this library is not maintained, we need to try a replacement even though it's tough.
We are facing the same issue, and I think I find a way to reliably replicate this bug. On Android 9 emulator, almost every time I restart the app, the data is erased.
Hello, we had the same problem on production, data randomly erased on IOS. How we can resolve the issue? Someone has solution?
I'm also in the progress of debugging a possible variant of this issue. I wanted to share what I have so far, and might edit to update this comment in the near future if I find out more. The solution you may want to implement depends on your application needs I suppose. I don't have a good solution yet for my own actual current production scenario, partially because I'm unsure yet which variant I'm (mostly) experiencing.
transforms
(confirmed in production for me)If I introduce an exception in the transforms
that are applied after reading from storage, things will blow up and store data might get reset to initial state. In pseudo-code (I don't have a clear repro at hand yet, have to extract that from our prod app):
const persistConfig = {
key: 'root',
storage: YourStorageHere,
transforms: createTransform(
(inboundState, key) => ({ ...inboundState, myRandomValue: Math.trunc(Math.random() * 10) }),
(outboundState, key) => {
if (myRandomValue[0] === 5) throw "simulated random error";
return { ...outboundState };
},
{ }
};
So every 1 in 10 times storage will have myRandomValue === 5
and when it is read from storage the tranform will crash. This simulates for example a more realistic NullReferenceException in the outbound
function. And it will cause the state to be reset to initial state for your store.
Earlier comments, most notably the one by @ssorallen already mention it might have to do with timeout
s. I can in fact confirm that this is the case! I wrote this storage
implementation and used it on snack.expo.dev to see the behavior in action. First, the code:
const getDelay = () => Math.trunc(Math.random() * 10000);
function log(...args) {
console.log(`[${new Date().toISOString().substring(11)}]`, ...args);
}
const persistConfig = {
key: 'root',
storage: {
getItem(key) {
const delay = getDelay();
log(`[Delayed ${delay.toString().padStart(6, ' ')}] Getting`, key);
return new Promise(resolve => {
const raw = localStorage.getItem(key);
log(`[Delayed ${delay.toString().padStart(6, ' ')}] Getting resolved to`, raw);
return setTimeout(() => {
log(`[Delayed ${delay.toString().padStart(6, ' ')}] Getting resolves now`);
resolve(raw);
}, delay); // β !!! 50/50 chance this goes above the 5000ms timeout!
});
},
setItem(key, value) {
log(`[Delayed ${delay.toString().padStart(6, ' ')}] Setting `, key, value);
return new Promise(resolve => {
log(`[Delayed ${delay.toString().padStart(6, ' ')}] Setting resolved `, key, value);
return setTimeout(() => {
log(`[Delayed ${delay.toString().padStart(6, ' ')}] Setting resolves now `, key, value);
localStorage.setItem(key, value);
resolve();
}, 50); // Small delay
});
},
removeItem(key) {
log('Removing', key);
return new Promise(resolve => {
log('Removing resolved', key);
return setTimeout(() => {
log('Removing resolves now', key);
localStorage.removeItem(key);
resolve();
}, getDelay()); // Random delay
})
}
},
};
This storage implementation retrieves the state with a random delay of 0-10 seconds. If it exceeds the default redux-persist timeout of 5 seconds, state will get reset. So it is a roughly 50/50 chance of getting wiped.
You can check my 'Snack' example live to see this in action. Increment the counter a few times to persist that state. Then turn preview off and on again to "reload" the app. The state gets read from storage, and you have 50/50 chance of it being either loaded, or reset to initial state.
If you change the getItem(...)
method for the storage implementation to this:
getItem(key) {
const delay = Math.trunc(Math.random() * 1000);
return new Promise((resolve, reject) => {
const raw = localStorage.getItem(key);
if (delay % 2 === 0) {
console.log('πππ Simulating error!');
return setTimeout(() => reject('error simulated'), delay);
}
return setTimeout(() => { resolve(raw); }, delay);
});
},
It will randomly fail about 50% of the time (in this example getItem
always resolves within the default timeout of 5000. And that also has the effect of resetting to initial state.
Depending on your storage implementation (we use react-native-encrypted-storage
in our production app) there might be scenarios where this happens. Who knows, Android or iOS might have all sorts of reasons to throw a (probably temporary) error while accessing storage. For example the SD card might not be ready for reading, would (I imagine) not be an uncommon scenario.
Just to make my comment a bit more "complete" I've added this variant. The Original Post of this issue already mentions and links a related issue that suggests the same behavior might be caused by a filled up storage causing a reset. If you're reading this, you might want to consider if this is your issue. For my production scenario it's an unlikely cause, as our state is fairly small in size.
PS. This might be different from variant "2B" above because it would happen during setItem
, but in essence it would also be a bit of the same: errors may occur during storage operations.
Introduction
We are building a React Native app, and we are using redux-persist together with redux-persist-filesystem-storage to store data on the phone.
The issue
We are experiencing that sometimes after an app update (via TestFlight and Play Store) all the app data just is erased. The app is simply back to initial state. This can happen for both iOS and Android.
Steps to reproduce
Well, this is where it get's annoying.. We haven't yet been able to get the exact steps to reproduce the issue. It just happens sporadically. We have around 100 beta testers, and ~20 people have reported this issue. Both on iOS and Android, and there was no link between a specific app update version or anything.
Similar issues
We have of course looked around on GitHub, trying to find similar issues. At first we thought https://github.com/rt2zz/redux-persist/issues/199 was our issue, so that's why we switched to redux-persist-filesystem-storage from AsyncStorage. But that didn't help. And it looks like other people are still experiencing it as well: https://github.com/robwalkerco/redux-persist-filesystem-storage/issues/2
Additional information
redux-persist
version: 4.9.1redux-persist-filesystem-storage
version: 1.10Final note
Although this issue is somewhat vaguely described, I'm submitting it to raise awareness, because we think it's a rather important issue if it really IS an existing bug. Maybe someone out there is looking for help with the same issue - so here it is.
I have created the same issue on redux-persist-filesystem-storage: https://github.com/robwalkerco/redux-persist-filesystem-storage/issues/14