This is pointed towards the redux and Apollo branches: as things are so now, a page refresh will return the store to it's initial state.
Has anyone implement something like redux-persist with this boilerplate?
How can I make the redux store state persist across a page refresh with SSR?
What I have done so far:
import { createStore, applyMiddleware, compose } from 'redux';
import { autoRehydrate } from 'redux-persist';
{...}
if (__DEV__) {
middleware.push(createLogger());
// https://github.com/zalmoxisus/redux-devtools-extension#redux-devtools-extension
let devToolsExtension = f => f;
if (process.env.BROWSER && window.devToolsExtension) {
devToolsExtension = window.devToolsExtension();
}
enhancer = compose(
applyMiddleware(...middleware),
autoRehydrate(),
devToolsExtension,
);
} else {
enhancer = compose(
applyMiddleware(...middleware),
autoRehydrate(),
);
}
{ ... }
/src/actions/persisteRehydrate
import { PERSIST_REHYDRATE } from '../constants';
export function updateUserInfo({ name, value }) {
return {
type: PERSIST_REHYDRATE,
payload: {
name,
value,
},
};
}
/src/reducers/persistRehydrate
import { PERSIST_REHYDRATE } from '../constants';
export default function persistRehydrate(state = {}, action) {
switch (action.type) {
case PERSIST_REHYDRATE:
return {
...state,
[action.payload.name]: action.payload.value,
};
default:
return state;
}
}
import { persistStore } from 'redux-persist';
{ ... }
const store = configureStore(undefined, {
apolloClient,
fetch,
history
});
persistStore(store, {storage: localForage}, () => {
console.log("resolved!");
});
const context = {
// Enables critical path CSS rendering
// https://github.com/kriasoft/isomorphic-style-loader
insertCss: (...styles) => {
// eslint-disable-next-line no-underscore-dangle
const removeCss = styles.map(x => x._insertCss());
return () => { removeCss.forEach(f => f()); };
},
// For react-apollo
client: apolloClient,
// Initialize a new Redux store
// http://redux.js.org/docs/basics/UsageWithReact.html
store: store,
fetch,
storeSubscription: null,
};
{ ... }
Rehydration appears to be working with the above code, but the problem now is that it takes some time to rehydrate the store on the client side, routes start evaluating before the store is rehydrated which causes problems, so the application must wait for rehydration before continuing
Hey @tim-soft , thanks for posting.
What version of redux-persist you were using? I just implemented v5 of it on my project with the foillowing code changes to configureStore.js.
import { createStore, applyMiddleware, compose } from 'redux';
import thunk from 'redux-thunk';
import rootReducer from '../reducers';
import createHelpers from './createHelpers';
import createLogger from './logger';
import {persistStore, autoRehydrate} from 'redux-persist'
export default function configureStore(initialState, helpersConfig) {
const helpers = createHelpers(helpersConfig);
const middleware = [thunk.withExtraArgument(helpers)];
let enhancer;
if (__DEV__) {
//Thunk and redux-persist
middleware.push(createLogger());
// https://github.com/zalmoxisus/redux-devtools-extension#redux-devtools-extension
let devToolsExtension = f => f;
if (process.env.BROWSER && window.devToolsExtension) {
devToolsExtension = window.devToolsExtension();
}
enhancer = compose(applyMiddleware(...middleware), autoRehydrate() , devToolsExtension);
} else {
enhancer = compose(applyMiddleware(...middleware) , autoRehydrate() );
}
// See https://github.com/rackt/redux/releases/tag/v3.1.0
const store = createStore(rootReducer, initialState, enhancer);
// begin periodically persisting the store
persistStore(store)
// Hot reload reducers (requires Webpack or Browserify HMR to be enabled)
if (__DEV__ && module.hot) {
module.hot.accept('../reducers', () =>
// eslint-disable-next-line global-require
store.replaceReducer(require('../reducers').default),
);
}
return store;
}
Working well so far, no need to any additional reducer/actions.
Next problem I'm tackling is the fact that page redirection based on store parameters will kick in before the rehydrate can happen, any advice is welcome.
@lvian Redux Persist v5 does not have autoRehydrate so it might be your are using v4?
How to use redux-persist in this project? Can show demo?
This may be problematic.
In general, you should prepare state on server side by calling redux actions based on session or user state.
This state should be updated incrementally by client on server, so you then should be able to re-create state on server without data loss.
On client, you can store really small subset of all, so it can help prevent data loss when connection is lost.
Another way is to skip server side rendering, instead you can return only blank page with boot code and do the rest on client only. This makes sense if user is authenticated and page with private data is being prepared.
Still, keep in mind that if user load page on different device or browser, client only data will be lost or may cause conflict in data model.
Persisting redux has a few practical benefits. Imagine storing some UI state in redux, if you refresh the page then this state is reset to it's initial value which can be annoying for the user -- storing this data elsewhere is annoying ¯\_(ツ)_/¯
The solution has been suggested more or less in this thread so I'll go ahead and close.
What about saving some information in url? If you think about selected tab or similar.. That way sharing an url will have expected behavior
@langpavel saving things like tab keys in the URL usually make sense. React components lose their state when the re-render, I'm thinking about situations where there is _a lot_ of UI state to remember across re-renders/refreshes that I also wouldn't want to replicate when users share URLs. Managing a lot of stuff in redux is a lot more practical than URL vars/fragments