Scope
Our product listing page is async loaded by react ajax calls. We want to handle a 'browser back button' scenario that when the user click back, the ajax parameter will persist and the same products can be loaded on the page. A common problem when making ajax call that would change a page content after the page loaded.
Code
Setup
In order to achieve what we want, we need to first persist our state somewhere, then retrieve and rehydrate the state.Persist
As opposed to the blacklisting example, we are doing it by whitelisting.
persistStore(store, {whitelist: ['myReducer1', 'myReducer2']}, () => { console.log('redux-persist rehydrated') })
Redux-persist will raise an action called 'persist/REHYDRATE' and we need to create a reducer to handle this.
This will persist our state in a state called reduxPersist. It is a preserved keyPrefix for the localstorage default key, so that autoRehydrate can retrieve the persisted state.
import {REHYDRATE} from 'redux-persist/constants' const reduxPersist = (state, action) => { if (state == undefined) { return Object.assign({}, state, null) } switch (action.type) { case REHYDRATE: if (action.payload.myReducer1 && action.payload.myReducer2) { return Object.assign({}, state, { myReducer1: action.payload.myReducer1, myReducer2: action.payload.myReducer1 }) } return state } } export default reduxPersist
This will persist our state in a state called reduxPersist. It is a preserved keyPrefix for the localstorage default key, so that autoRehydrate can retrieve the persisted state.
Only myReducers are persisted, others are not persisted. |
Auto Rehydrate
We can rehydrate a state either by using autoRehydrate or manually doing it.
To setup autoRehydrate, we just need to add an enhancer to our store. Then our state tree is automatically rehydrated when we reload the page.
const store = createStore ( combinedReducer, undefined, compose( applyMiddleware( thunk, logger ), autoRehydrate() ) )
Manual Rehydrate
Who likes driving manual transmission these days? I do and it gives me more granular control over what I want to achieve.
To setup manual rehydrate is not as hard as it sound. We just need to pass in the reduxPersist payload in the action and utilize it in the reducer.
In the action,
In the reducer,
In the action,
return { type: 'UPDATE_MY_FIELDS', myFields: somefields, payload: payload }
In the reducer,
case 'UPDATE_MY_FIELDS': var shouldReadCache; // some boolean custom logics if (shouldReadCache) { return Object.assign({}, state, { myFields: action.payload.myReducer1.myFields }) } else { return Object.assign({}, state, { myFields: action.myFields }) }
Conclusion
Auto rehydrate works out of the box like a charm if implemented correctly, but manual rehydrate still has its place especially for more complicated scenarios. I did not run into race conditions like other people did, but our fetch calls are usually wrapped by using the promise.