Open eneskaya opened 8 years ago
I have the same experience. Trying to figure out what is wrong.
What I found out so far is, that it partially works when I pass undefined
as the initial state, when calling createStore
const store = createStore(reducersCombined, undefined, compose(...enhancers))
persistStore(store, { storage: AsyncStorage })
My initial state looks something like the following:
const initialState = {
user: {
logged_in: false
},
questions: [
{ question_title: 'First question title', another_attribute: '...' },
{ question_title: 'Second question title', another_attribute: '...' }
]
}
What happens here is, that when I change the primitive value logged_in
it gets persisted. Only the array questions
is not being persisted. Is this the intended behaviour?
When passing undefined as initial state, I can see the REHYDRATE event being raised, and I can see the payload looking correct, but the next state is totally wrong (missing stuff like with @eneskaya).
If I don't pass undefined as initial state, the REHYDRATE event is raised but nothing is done to the state, although the payload is correct.
@AymericG where/how do you see the REHYDRATE event being raised?
@eneskaya In the console, I use redux-logger.
Hm, this sounds like a bug in autoRehydrate. Can you try adding autoRehdyrate({log: true})
and then report the log output here? Also what version of redux-persist are you on?
@rt2zz I'm on 4.0.0-alpha6 and the output is as follows:
redux-persist/autoRehydrate: 1 actions were fired before rehydration completed.
This can be a symptom of a race condition where the rehydrate action may
overwrite the previously affected state.
Consider running these actions after rehydration:
redux-persist/autoRehydrate: sub state for key `questions` is falsy but initial state is an object, skipping autoRehydrate.
What does sub state for key 'questions' is falsy
mean?
Edit:
"react": "15.3.2",
"react-native": "0.33.0",
that message logs when https://github.com/rt2zz/redux-persist/blob/master/src/autoRehydrate.js#L55 hits, meaning the inbound state is either null undefined or false. Is questions the only reducer which is failing to rehydrate?
I'm noticing the same behavior. Would you like me to provide the logs?
Edit: Now that I think of it, I believe this would be the expected behavior when using multiple reducers and combineReducers. Each of my reducers has an initialState and the rootReducer also has an initialState. Previously, I would also set the initialState when creating the store, which would cause rehydrate to not update nested states/objects.
However, by setting createStore(rootReducer, initialState, compose())
to createStore(rootReducer, undefined, compose())
, redux-persist is now able to rehydrate the state properly and I still have an initialState for first-time run.
interesting. in the former case, how did you construct initialState? did the shape of that value look exactly like the shape of the resulting reducer tree?
@rt2zz I used default params to construct it
Previously I had
configureStore(initialState = {}) {
const store = createStore(
rootReducer,
initialState,
compose(
autoRehydrate(),
applyMiddleware(
thunk,
...middleware,
createActionBuffer(REHYDRATE)
)
)
);
I have an isomorphic app and I was providing an actual initialState to configureStore when the app is mounted client side (after SSR).. Im not sure why I thought I had to provide a default empty object initialState when calling configureStore without an actual state to restore.
Same issue here with SSR. Doesn't seem to dispatch
the initial state coming from the server. Also, I'm whitelisting one reducer.
I believe I'm running into this, too. Currently testing on react-native using an ios app.
Packages are as follows:
"react": "15.4.1",
"react-native": "0.39.0",
"react-redux": "4.4.5",
"redux": "3.5.2",
I'm also unable to get any logs to print, despite having used
autoRehydrate({log:true})
in the compose function for the middleware I'm using (which consists of this package and thunk.
TL;DR: provide undefined as the second param to createStore.
I've only noticed the behavior when I provide an initial state as the second param to createStore. If you pass undefined or null then it rehydrates correctly. I haven't reviewed the code in depth, but I'd imagine it doesn't restore state objects where a value already exists in the store state during creation.
When you have multiple reducers and you provide an initial state to them, then your first run state will shape correctly. Then subsequent launches will restore the state from redux-persist
Strangely enough, that's what I'm doing. Here's a simplified snippet:
import { createStore, applyMiddleware, combineReducers, compose } from 'redux';
import thunk from 'redux-thunk';
import {AsyncStorage} from 'react-native'
import {persistStore, autoRehydrate} from 'redux-persist'
import { app } from './modules'
import { login} from './modules'
const middleware = compose(
applyMiddleware(thunk),
autoRehydrate({log:true}),
);
export default (data = {}) => {
const rootReducer = combineReducers({
//every module's reducer defined here
[app.NAME]: app.reducer,
[login.NAME]: login.reducer
})
return createStore(rootReducer, undefined, middleware)
persistStore(store, {storage: AsyncStorage})
}
Shoot. persistStore
happens after return? Good idea, self.
So if i create my reducers like this:
const reducers = (state = {}, action) => {
switch (action.type) {
case 'ADDED':
....
}
}
Does that state = {}
default declaration count as initializing the state? I'm wondering if that's why my store is not rehydrating.
No that's fine.. That's how I do it. You just cannot pass a default initial state to the createStore function On Jan 5, 2017 8:52 AM, "JulianKingman" notifications@github.com wrote:
So if i create my reducers like this:
const reducers = (state = {}, action) => { switch (action.type) { case 'ADDED': .... } }
Does that state = {} default declaration count as initializing the state? I'm wondering if that's why my store is not rehydrating.
— You are receiving this because you commented. Reply to this email directly, view it on GitHub https://github.com/rt2zz/redux-persist/issues/189#issuecomment-270648388, or mute the thread https://github.com/notifications/unsubscribe-auth/AEeBdpqdSzs0WM7y2E0pCmQlwsm4f2XFks5rPPWEgaJpZM4KR1gi .
@JulianKingman same as you. Everything looks fine, I just see that the autoRehydrate is skipped because an action is triggered. I don't know where because even the redux-logger doesn't say anything before this log from autoRehydrate..
const app = combineReducers({
...
carts
});
const enhancers = compose(
applyMiddleware(
thunk,
loadedSoundsMiddleware,
redux-logger,
createActionBuffer("persist/REHYDRATE")
),
autoRehydrate({log: true})
);
// Create and persist the store to the local storage
const store = createStore(app, undefined, enhancers);
// We store only the checkout process with cart etc.
const persistConfig = {
whitelist : ["carts"]
};
persistStore(store, persistConfig);
export default store;
And my carts store looks like that :
const initialState = {
all: {}
};
const planReducer = (state, action) => {
if (typeof state === 'undefined') {
return initialState;
}
switch (action.type) {
case ...:
...
}
};
export default planReducer
This is my log from the autoRehydrate
redux-persist/autoRehydrate: 1 actions were fired before rehydration completed. This can be a symptom of a race condition where the rehydrate action may overwrite the previously affected state. Consider running these actions after rehydration:
redux-persist/autoRehydrate: sub state for key
carts
modified, skipping autoRehydrate.
I ended up manually implementing the reducer, and now it works fine, more info here: https://github.com/rt2zz/redux-persist/issues/244#issuecomment-270655356
Thanks @JulianKingman, I fixed on my side adding this piece of code in my reducer. It's extracted from the READ_ME, that's not great to add a case in the switch but it works :
import {REHYDRATE} from 'redux-persist/constants'
case REHYDRATE:
var incoming = action.payload.carts; // Carts is the name of the reducer
if (incoming) return {...state, ...incoming};
return state;
There is 2 things I did to solve this without having to resort to an opt-in per reducer rehydrate.
create
function rather than the store and creating the store in my entry index.js. Simply exporting my store instead of a function worked.~Keep in mind that doing 2 alone did not work, I had to do 1 as well.
EDIT: only point 2 is necessary
@HarrisRobin can you show a code snippet of what you mentioned above?
@HarrisRobin I too would be interested in seeing any code snippets you can share.
I was able to correct this without having to use undefined as the initial state. Here's the code
In index.android.js
export default class AppName extends Component {
constructor(){
super();
this.state = {rehydrated: false}
}
componentWillMount(){
persistStore(store,{storage: AsyncStorage},()=> {
console.log("rehydrated");
this.setState({rehydrated:true});
});
}
render() {
if(this.state.rehydrated){
console.log(store.getState());
return (
<App store={store}/>
)
}
else {
return(
<View>
<Text style={{marginTop:100, alignSelf:'center'}}>Setting up...</Text>
</View>
)
}
}
}
AppRegistry.registerComponent('AppName', () => AppName);
I'm making sure that the rehydration is complete in componentWillMount() and then passing the store to the provider as a prop. While it rehydrates, I'm displaying a text "Setting up.." on the screen.
Store configuration
function configureStore(initialState) {
const enhancer = compose(
applyMiddleware(
thunkMiddleWare,
loggerMiddleWare
),
autoRehydrate()
);
return createStore(reducer, initialState, enhancer);
}
I'll share my experience here:
A combination of @bhavyanth7777 and @JulianKingman solution worked for me.
I had to make use of the callback after rehydration to load my main component, but also implement the persist/REHYDRATE
in my reducer manually. It looks kinda hacky, but it works and, most importantly, I can still make use of initialState
, which is cool.
Struggled with this issue tonight. Finally figured out I had an error in my reducer. In my reducer I was writing:
Object.assign(state, {new_value: 'blah blah'})
as oppososed to:
Object.assign({}, state, {new_value: 'blah blah'})
See this SO post for more detail: https://stackoverflow.com/questions/33828267/why-do-redux-examples-pass-empty-object-as-first-object-assign-argument
I'm using v5 and am seeing the problem as well. I noticed this issue can occur if you have state mutations triggered on app load (before rehydration is complete).
Example:
export default (state = defaultState, action) => {
// Standard switch handler
let newState = handleAction(state, action)
// This always runs *AFTER* the reducer's other state changes. E.g. some flag setting any time a redux event fires
return someMutation(newState);
}
The above somehow causes the rehydrate to be aborted, despite having the same state shape (in my case) before & after the mutation. It's probably checking to see if the reference object has changed.
There are a few ways to fix it:
export default (state = defaultState, action) => {
let newState = handleAction(state, action)
// handleAction default action handler returns false or null. Could check obj equivalence instead if you'd rather return the passed-in state as the default handler.
if (!newState) {
return state
}
return someMutation(newState);
}
Don't make your mutation global. Either thunk all your desired action creators with this "global" action, or wrap the mutation on each handler you want it on. This could also make it a bit more performant (at the cost of being uglier).
export default (state = defaultState, action) => {
switch (action.type) {
case 'ACTION_A': return someMutation(
// Your standard handler for this action
)
case 'ACTION_B': return someMutation(
// Your standard handler for this action
)
case default:
// If you touch put a mutation here, it will break rehydration.
return state
}
As mentioned by others in the thread, add a rehydrate handler that purposefully doesn't touch state:
import { REHYDRATE } from 'redux-persist'
export default (state = defaultState, action) => { // Workaround to prevent interruption of rehydration if (action.type === REHYDRATE) { return state }
return someMutation(handleAction(state, action));
}
All of these options feel a bit hacky. Maybe we could tell redux-persist to force rehydrate regardless of the incoming state?
EDIT: Note an alternative to the above is by creating middleware, which is perhaps a better cross-cutting approach to this sort of behavior. I use the above as examples of where rehydration can be aborted.
@leguma I am open to having redux-persist always rehydrate, or at least exposing this behavior somehow. It is definitely a source of confusion. I hate to add more config options though, so perhaps this should be a separate stateReconciler?
FWIW we do warn when this happens while debug: true
...
It is unfortunate because the underlying complexity around this is actually quite low, it is just an issue with communication / api ergonomics. Even persistReducer vs persistCombineReducers I think leads to confusion. We need a new naming scheme.
I have been struggling with this for weeks, see: https://stackoverflow.com/questions/47971918/using-connect-with-react-redux-and-redux-persist Tried @leguma 3 ways without avail. Please anyone can help me on this?
I had to pass config from server to app in initial state (I use SSR), but redux-persist worked only if i pass {} in initial state in configure-store.js. I fixed this by blacklisting state field I needed to pass from server with initial state, and passing state only with this field as initial state:
function configureStore(isHotLoaderRequired = false) {
return (initState = {}, history = null) => {
const store = createStore(
persistedReducer,
{ settings: initState.settings }, // <- here! If I pass here initState, redux-persist not works
applyMiddleware(
routerMiddleware(history),
thunk
)
);
. . .
}
this is how i create my store
const persistConfig = {
key: 'root',
storage: storage,
stateReconciler: autoMergeLevel2 // see "Merge Process" section for details.
};
const pReducer = persistReducer(persistConfig, rootReducer);
export function configureStore(history) {
const middleWares = [thunk];
middleWares.push(createLogger());
const middleware = applyMiddleware(...middleWares);
return createStore(pReducer, middleware);
}
export const persistor = persistStore(configureStore());
This is how I initialize my app
import { PersistGate } from 'redux-persist/lib/integration/react';
import { persistor, configureStore } from './store';
<Provider store={configureStore()}>
<PersistGate loading={<Text>Loading</Text>} persistor={persistor}>
<Root>
<AppNavigator />
</Root>
</PersistGate>
</Provider>
Checked by passing undefined
as the second argument. Still no luck. Please tell me what I'm doing wrong.
I created my store like this..
configureStore.js:
import { createStore, compose, applyMiddleware } from 'redux';
import thunk from 'redux-thunk';
import {logger} from 'redux-logger';
import allReducers from '../reducers';
const middleware = [
thunk,
logger
];
const composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose;
export default function configureStore(){
let store = createStore(
allReducers,
{},
composeEnhancers(applyMiddleware(...middleware))
);
console.log("Store created here"+store);
return store;
}
reducers/index.js:
import {persistCombineReducers} from 'redux-persist';
import storage from 'redux-persist/es/storage';
import autoMergeLevel2 from 'redux-persist/es/stateReconciler/autoMergeLevel2';
import autoMergeLevel from 'redux-persist/es/stateReconciler/autoMergeLevel1';
import Authentication from './Authentication';
import Homeworks from './Homeworks';
import ClassConfig from './ClassConfig';
const config = {
key :'root',
storage,
stateReconciler: autoMergeLevel2,
// stateReconciler: autoMergeLevel,
whitelist:['authentication'],
blacklist:['homeworks','classconfig']
}
const allReducers = persistCombineReducers(config,{
authentication: Authentication,
homeworks: Homeworks,
classconfig: ClassConfig
});
export default allReducers;
Authentication.js:
import { LOGIN, LOGOUT, GET_USER_DETAILS,REQUEST_FAIL } from '../actions/actionTypes';
// import { REHYDRATE } from 'redux-persist';
const DEFAULT_AUTHENTICATION = {
email: null,
name: null,
token: null,
signedIn: false,
error: null,
userDetail:{},
logoutmessage:null
}
const Authentication = (state = DEFAULT_AUTHENTICATION, action) =>{
switch(action.type){
case LOGIN:
console.log("reducers/Authentication/LOGIN called"+state);debugger
return{
...state,
token: action.response.success.token,
name: action.response.success.name,
signedIn: true
}
case LOGOUT:
console.log("reducers/Authentication/LOGOUT called");
return{
...state,
logoutmessage:action.response.success,
token : null,
name : null,
signedIn : false
}
case GET_USER_DETAILS:
console.log("reducers/Authentication/GET_USER_DETAILS called");
return{
...state,
userDetail: action.response.success,
email: action.response.success.email,
name : action.response.success.name
}
case REQUEST_FAIL:
console.log("reducers/Authentication/REQUEST_FAIL called");
return{
...state,
error: action.error,
}
case "persist/REHYDRATE":
const data = action.payload;
if (data)
return {
...state,
...data.authentication
} //Should I compulsorily write this case?If i dont write it,the payload is not replacing the next state while rehydrating the store(i.e store gets only initial state).Here my doubt is for each whitelist state should i include the line as ...data.whitelist1 , ...data.whitelist2, ...data.whitelist3...?
default:
console.log("default/reducers/Authentication called");
return {
...state
}
}
}
export default Authentication;
Login.js:
//import stmts
const mapStateToProps = (state) =>{
return{
authentication :state.authentication,
}
}
const mapDispatchToProps = (dispatch) => {
return{
onSubmit : (credentials) => {
dispatch(login(credentials));
}
}
}
class Login extends Component {
constructor(props){
super(props);
this.state = {
email:null,
password:null,
otp:null
}
}
componentDidMount(){
Toast.show('Welcome to Edfish', Toast.LONG);
}
submit(callback){
let credentials={};
credentials.email=this.state.email,
credentials.password=this.state.password,
this.props.onSubmit(credentials);
callback();
}
render() {
return (
<Container >
<StatusBar backgroundColor={styles.mystatusbar} barStyle='dark-content'/>
<Content contentContainerStyle={{ justifyContent: 'center', flex: 1,marginLeft:15,marginRight:15
}}>
<Text style={{alignSelf:"center"}}>EDFISH</Text>
<Label style={{paddingTop:25,paddingBottom:15,fontWeight:'800'}}>Enter email id:</Label>
<View style={[styles.loginInput,styles.h_50]}>
<Input placeholder="myemail@gmail.com" onChangeText={(email)=>{
this.setState({
email:email
});
}}
onSubmitEditing={()=>{
this.refs.password._root.focus();
}}
blurOnSubmit={false}/>
</View>
<Label style={[styles.pad_top_25,styles.pad_bot_15,styles.font_w8_800]}>Enter your password:
</Label>
<View style={[styles.loginInput,styles.h_50]}>
<Input placeholder="your password" secureTextEntry={true} onChangeText={(password)=>
{
this.setState({
password:password
});
}}
onSubmitEditing={(event)=>{
this.submit(function() {
console.log('huzzah, I\'m done!');
});//The if stmt gets checked before my reducers/LOGIN gets completed the changes in the store
if(this.props.authentication.error !== null){
this.props.navigation.navigate('Home')
}
else{
Toast.show('Incorrect username or password');
}
}} ref="password"/>
</View>
<View style={{paddingTop:25,paddingBottom:15,}}>
<Button block onPress={ ()=>{
this.submit(function() {
console.log('huzzah, I\'m done!');
});debugger
if(this.props.authentication.error !== null){
this.props.navigation.navigate('Home')
}
else{
Toast.show('Incorrect username or password');
}
}} style={{
height:50,
borderRadius:5,
backgroundColor:'#3883c0'
}}>
<Text style={{alignSelf:'center',
color:'white',
fontSize:18,
fontWeight:'500',
paddingTop:10,
paddingBottom:10}}>Login</Text>
</Button>
</View>
</Content>
</Container>
);
}
}
export default connect(mapStateToProps,mapDispatchToProps)(Login);
App.js/render method:
const store = configureStore();
store.subscribe(() =>
console.log('Store State: ', store.getState())
);
const persistor = persistStore(
store,
{},
()=>{
console.log('rehydration completed!!!!', store.getState());
}
);//I have checked by passing undefined also!
render() {
return (
<Provider store={store}>
<PersistGate loading={null} persistor={persistor} >
<MyRoot/>
</PersistGate>
</Provider>
);
}
Now here my all confusion is that .. when i close the app and simple opens store state is persisted.If i reload the app store gets initial state.I am struggling to figure out the problem.Please someone tell me the solution for the comments i mentioned in the code.
Thanks in advance.
return createStore(pReducer, middleware);
@yasserzubair
second argument in this call should be an initial state (you can also pass undefined
or empty object) return createStore(pReducer, undefined, middleware);
I hope this works :)
Hi,
Iam using redux-offline,
I am facing same issue as @Johncy1997
Below is how i am creating my store
let customConfig = {
...offlineConfig,
persistOptions:{
key: 'root',
transforms: [immutableTransform()],
},
returnPromises : false,
persistCallback : () => {this.setState({rehydrated : true})},
};
const { middleware, enhanceReducer, enhanceStore } = createOffline(customConfig);
let middlewares = applyMiddleware(offlineCommitHandler,thunk,middleware,);
store = createStore(enhanceReducer(IndexReducer),undefined,compose(enhanceStore,middlewares,persistAutoRehydrate({log:true})));
I have multiple reducers The issue occurs only in one reducer,
I placed a debugger in autoRehydrate, when opening the app first time it merges the data for that reducer, When opening the app second time inbound state for that reducer is null.
Hi @gogoku, already had this issue when I had a mutation in one of my reducer. You could plug a mutation detector in redux middleware to be sure nothing mutate.
Hi @DonovanCharpin , I checked for mutations using the library redux-immutable-state-invariant. Also i manually wen through the code for the reducer and i couldn't find any mutations occurring.
Thanks @DonovanCharpin
Actually my issue was that the data was hitting the storage limit for asyncStorage in android and the data was being deleted.
Details can be found at this issue #199
Solved this using file system storage instead of asyncStorage https://github.com/robwalkerco/redux-persist-filesystem-storage
Same for me, any help here?
Check out my earlier comments regarding changing state during REHYDRATE events. If you've determined that you're definitely not doing that, then check to see if @gogoku's issue is related.
For me, my issue was that I had a default reducer handler that was changing some state (setting an expiresAt property and some other things). The REHYDRATE event, therefore, was causing a state mutation. In this case, redux-persist aborts (or perhaps there's a race condition), resulting in the rehydrated state being lost.
My solution was simply to prevent any state changes during the REHYDRATE event:
export default (state = defaultState, action) => {
// do not mutate state during REHYDRATE!
if (action.type === REHYDRATE) {
return state
}
// handleAction is where the guts of this reducer lives.
// wrapperMutation happens AFTER the reducer logic; it _always_ mutates state (in this case, updating my expiresAt property).
// This would break rehydration if not for the above if statement.
return wrapperMutation(handleAction(state, action));
}
@leguma Thanks, yep the problem was in my reducer. My mistake was in "default: return { ... state }" not "return state"
@OlegPanfyorov worked for me as well. Does someone know why this solution works?
If anyone wants to know why the original issue in this thread was happening (specifically: any instance of initialState
being passed manually to createStore
causes rehydration to not work), then it's pretty simple: redux always calls an @@INIT
action when you pass this argument in (this is correct, and is documented by redux in the API for createStore
).
redux-persist
then notices that the store was updated, and persists your initial state to storage.
It then tries to hydrate your initial store with the state you literally just persisted, which is obviously a no-op.
I'm not totally sure that this is what's happening, but I'm fairly confident.
I personally haven't found a fix yet that meets all my needs, but I can see a potential few:
redux-persist
knows not to persist in response to the @@INIT
action.Update: ~the solution I chose for myself was to add a transform for the states I cared about, which detected whether this was an initial Redux commit, and returned an empty persist object ({}
) if so (well, technically I returned the fields I cared about with the interfering fields stripped out, but your use case would likely be simpler).~
~This wasn't ideal, because I have to make such a transformer for each state and condition I care about, but that's also not terrible because you're essentially self-documenting every such case.~
This turned out not to work, because redux-persist
doesn't have a method (as far as I can tell) for resolving merge conflicts in the inbound route to storage (in the same way that it does for outbound data from storage to Redux), so any act of persistence is all-or-nothing. I can see some easy fixes where either that method is added to the redux-persist
API, the whitelist
and blacklist
config settings become functions that can take the incoming data to decide whether to persist (rather than just using key names), etc.
Unfortunately, I'm a little short on bandwidth; otherwise I'd implement all these myself and add a PR; so for now I did regrettably just have to refactor my app to avoid using preloadedState
to the best of my ability (or, specifically, avoid using the fields that are triggering the persistedReducer
s that I have set up).
In the meantime, I think it would probably be a good idea to add to the documentation that redux-persist works poorly if you must make use of preloadedState
in redux's createStore
method.
What is the workaround? I'm still confused. Sounds like the default usage is not working anymore. And I am also encountering this issue where the state is persisted when using the app but when reloading, the persisted state got overwritten by the initial state instead of loading them...
My workaround is to make it as a singleton, don't have time to dive into why this works but it works for me.
import { combineReducers, createStore, compose, applyMiddleware } from 'redux'
import { composeWithDevTools } from 'redux-devtools-extension/developmentOnly'
import { persistStore } from 'redux-persist'
//import reducers from '../reducers'
import persistReducer from '../utils/persistReducer'
let store = createStore(
persistReducer,
{},
composeWithDevTools()
);
let persistor = persistStore(store);
export default function create(initialState) {
if (!store) {
store = createStore(
persistReducer,
initialState,
composeWithDevTools()
)
}
return store
}
export function getPersistor(store) {
if (!persistor) {
persistor = persistStore(store)
}
return persistor
}
@leguma Thanks, yep the problem was in my reducer. My mistake was in "default: return { ... state }" not "return state"
Yeaaah you made my day. That was actually my mistake too. We should never return a shallow copy of the state when we actually don't want to change the state at all !
I found that my reducer was being called multiple times before my the hydration of redux-persist. That way it reset state with the initial state then used that to hydrate the app.
It can happen if you're importing a function which carries the store and the persist variable.
It's better just to directly import the store and persist vars to make sure that they aren't being called a number of times.
Update: ~the solution I chose for myself was to add a transform for the states I cared about, which detected whether this was an initial Redux commit, and returned an empty persist object (
{}
) if so (well, technically I returned the fields I cared about with the interfering fields stripped out, but your use case would likely be simpler).~~This wasn't ideal, because I have to make such a transformer for each state and condition I care about, but that's also not terrible because you're essentially self-documenting every such case.~
This turned out not to work, because
redux-persist
doesn't have a method (as far as I can tell) for resolving merge conflicts in the inbound route to storage (in the same way that it does for outbound data from storage to Redux), so any act of persistence is all-or-nothing. I can see some easy fixes where either that method is added to theredux-persist
API, thewhitelist
andblacklist
config settings become functions that can take the incoming data to decide whether to persist (rather than just using key names), etc.Unfortunately, I'm a little short on bandwidth; otherwise I'd implement all these myself and add a PR; so for now I did regrettably just have to refactor my app to avoid using
preloadedState
to the best of my ability (or, specifically, avoid using the fields that are triggering thepersistedReducer
s that I have set up).In the meantime, I think it would probably be a good idea to add to the documentation that redux-persist works poorly if you must make use of
preloadedState
in redux'screateStore
method.
For the late comer, with the stateReconciler
, I believe we can give up persisted state if it is conflicting with preloadedState
.
Great package! When I tried this, it seems that it always loads the initial state for me. My code looks like this:
What am I doing wrong here? Could be that I misunderstood something as I'm quite new to redux.