OS: Windows 10 Pro
redux-persist: "^4.8.0"
So after a reload of any of my Router paths is made, state becomes === undefined. But I want the state, as of that moment in time, to remain intact after a page reload. How do I ensure that this happens?
app.js
render(
<ApolloProvider store={store} client={client}>
{ /* Tell the Router to use our enhanced history */ }
<Router history={history}>
<Route path="/" component={App}>
<IndexRoute component={PhotoGrid} />
<Route path="/view/:postId" component={Single}></Route>
<Route path="/login" component={LoginUser}></Route>
</Route>
</Router>
</ApolloProvider>,
document.getElementById('root')
);
store.js
import { persistStore, autoRehydrate} from 'redux-persist';
import rootReducer from './reducers/index';
import localForage from 'localforage';
const middlewares = [thunk, client.middleware()];
const enhancers = compose(
applyMiddleware(...middlewares),
(typeof window.__REDUX_DEVTOOLS_EXTENSION__ !== 'undefined' || process.env.NODE_ENV !== 'production') ? window.__REDUX_DEVTOOLS_EXTENSION__() : (f) => f,
autoRehydrate(),
);
const defaultState = {
auth: {}
};
const store = createStore(
rootReducer,
undefined, // {}, // initial state
enhancers
);
// begin periodically persisting the store
persistStore(store, {storage: localForage});
export const history = syncHistoryWithStore(
browserHistory,
store
);
if(module.hot) {
module.hot.accept('./reducers/', () => {
const nextRootReducer = require('./reducers/index').default;
store.replaceReducer(nextRootReducer);
});
}
export default store;
index.js (Root reducer)
const rootReducer = combineReducers({
auth: tokenDetails,
logout: logOut,
routing: routerReducer,
apollo: client.reducer(),
});
export default rootReducer;
App.js
const allPostsCommentsQuery = graphql(All_Posts_Comments_Query, {
options: {
cachePolicy: 'offline-critical',
fetchPolicy: 'cache-first',
},
});
function mapStateToProps(state) {
return {
auth: state.auth
};
}
export const mapDispatchToProps = (dispatch) => {
return bindActionCreators(actionCreators, dispatch);
}
export default compose(
allPostsCommentsQuery,
connect(mapStateToProps, mapDispatchToProps)
)(Main);
tokenDetials.js (Reducer)
var tokenDetails = function(state, action) {
if (state === undefined) {
console.log('state = undefined, in tokenDetails.js, and has been reset to {}');
state = {};
}
switch (action.type) {
case 'Graphcool_Token':
const newState = { ...state, token: action.payload };
// console.log('newState = ', newState);
return newState;
default:
return state;
}
}
export default tokenDetails;
And do I need to do something like the following in each of my components to ensure that state is persisted, or is persistStore(store, {storage: localForage}); specified in my store.js enough?:
componentWillMount () {
persistStore(this.props.store, {storage: Localforage}, () => {
this.setState({ rehydrated: true })
})
}
Thanks to he wonderment that is logger the following two images show exactly what I'm experiencing. Note: in addition to store.js, specified in my initial question, I have also added the following code to each reducer:
import {REHYDRATE} from 'redux-persist/constants';
case REHYDRATE:
var incoming = action.payload.logOut
if (incoming) return {...state, ...incoming, specialKey: processSpecial(incoming.specialKey)}
return state
The first image reflects state directly after login and the users authToken has been captured:

The next image reflects state directly after a reload has occurred, clearly showing that authToken no longer exists:

How do I retain state as it existed prior to the reload?
Hi TheoMer, I had a similar issue when refreshing the browser. It was because the component render before the rehydratation was complete. Maybe you need to delay the render:
https://github.com/rt2zz/redux-persist/blob/master/docs/recipes.md
This helped me with that, I put the persistStore in my main component:
componentWillMount() {
persistStore(store, {}, () => {
this.setState({rehydrated: true})
})
}
This will ensure that the component re render when the store is rehydrated.
And do I need to do something like the following in each of my components to ensure that state is persisted, or is persistStore(store, {storage: localForage}); specified in my store.js enough?:
componentWillMount () {
persistStore(this.props.store, {storage: Localforage}, () => {
this.setState({ rehydrated: true })
})
}
I only put that in my main component, the router:
componentWillMount() {
persistStore(store, {}, () => {
this.setState({rehydrated: true})
})
}
render() {
if (!this.state.rehydrated) {
return <div>Loading...</div>
}
return (
<Provider store={store}>
<HashRouter history={history}>
<Switch>
<Route ... />
<Route ... />
<Route ... />
</Switch>
</HashRouter>
</Provider>
);
}
@glowind Solution found.
Rehydration was not working due to the order of autoRehydrate() in my enhancers in store.js. It MUST appear before devTools!
const enhancers = compose(
applyMiddleware(...middlewares),
autoRehydrate(),
(typeof window.__REDUX_DEVTOOLS_EXTENSION__ !== 'undefined' || process.env.NODE_ENV !== 'production') ? window.__REDUX_DEVTOOLS_EXTENSION__() : (f) => f,
);
I was able to then remove all of the rehydrate code from my main component (router), so it reads as follows:
class MainApp extends React.Component {
constructor(props) {
super(props);
}
render () {
return (
<ApolloProvider store={store} client={client}>
<Router history={history}>
<Route path="/" component={App}>
<IndexRoute component={PhotoGrid} />
<Route path="/view/:postId" component={Single}></Route>
<Route path="/login" component={LoginUser}></Route>
</Route>
</Router>
</ApolloProvider>
)
}
}
export default MainApp;
And all works as expected now.
For me below works fine.
...................
import configureStore from './store';
import {persistStore, autoRehydrate} from 'redux-persist'
const store = configureStore();
persistStore(store, {}, () => {
ReactDOM.render((
<Provider store={store}>
<Router>
<App/>
</Router>
</Provider>
), document.getElementById('root'));
registerServiceWorker();
})
Most helpful comment
For me below works fine.