window.initialReduxState is set at server site, but when calling reducer( window.initialReduxState, { type: DESERIALIZE } ) will reset serverState to default value. so even if we bootstrap user from server site and save the user data into state, the value we set into state, won't be used to create redux store. why calypso redux state is designed as this? the following code is where my thought from.
client/state/initial-state.js
function getInitialServerState() {
// Bootstrapped state from a server-render
if ( typeof window === 'object' && window.initialReduxState ) {
const serverState = reducer( window.initialReduxState, { type: DESERIALIZE } );
return pick( serverState, Object.keys( window.initialReduxState ) );
}
return {};
}
const loadInitialState = maybeAddSympathy( initialState => {
debug( 'loading initial state', initialState );
if ( initialState === null ) {
debug( 'no initial state found in localforage' );
initialState = {};
}
if ( initialState._timestamp && initialState._timestamp + MAX_AGE < Date.now() ) {
debug( 'stored state is too old, building redux store from scratch' );
initialState = {};
}
const localforageState = deserialize( initialState );
const serverState = getInitialServerState();
const mergedState = Object.assign( {}, localforageState, serverState );
return createReduxStore( mergedState );
} );
👋 Hey @videni to get a better sense of the cases involved, it's a little easier to look at the related test cases.
In general, with fresh server state, and recent local state, server state should override local state. Mainly since the recent server information is more useful. The persisted browser state is an enhancement, and usually is used to help display some data before addition fresh data is returned.
See this test for your noted case in question: https://github.com/Automattic/wp-calypso/blob/4eeabce98a6fa826d2b3df69e481b8f77682e835/client/state/test/initial-state.js#L192
Looks like the line in question was modified in https://github.com/Automattic/wp-calypso/pull/9820 so I can let @ockham and @budzanowski chime in if the current test descriptions are not reliable.
See also https://github.com/Automattic/wp-calypso/blob/master/docs/our-approach-to-data.md#data-persistence--2754- for a little more context on our special SERIALIZE and DESERIALIZE actions.
window.initialReduxStateis set at server site, but when callingreducer( window.initialReduxState, { type: DESERIALIZE } )will reset serverState to default value
@videni Are you sure that's the case? We definitely want reducer( window.initialReduxState, { type: DESERIALIZE } ) to deserialize server state, not to reset it.
Note that this is currenyl only really relevant for server-side rendered, logged out pages, such as wordpress.com/themes. Try opening that in a fresh incognito window, and use Redux DevTools to inspect state for the @@INIT action. You should see the themes.queries.wpcom.data.items state subtree pre-populated with theme data there:

So I think what you're seeing might rather be due to precedence of rehydrated server state vs locally persisted client state, as explained by @gwwar -- i.e. this specific line: https://github.com/Automattic/wp-calypso/blob/5e1905ab415447b65b8f0f551409a3a5e663a750/client/state/initial-state.js#L94
Note that I've coincidentally also recently found myself questioning whether we might actually want locally persisted client state to take precedence, which is why I filed #24758.
@gwwar @ockham , In my case I create a Product page, a product will be prefetched at server side and save to state.product, I explicitly specify the product will be hydrated into window.initialReduxState like this
serverRender method at render/index.js
const cacheableReduxSubtrees = [ 'documentHead' , 'product'];
the product reducer I created
import { combineReducers, createReducer } from 'state/utils';
import * as ActionTypes from './action-types';
export const product = createReducer(
{},
{
[ ActionTypes.PRODUCT_RECEIVE ]: ( state, action ) => {
return {
...action.product
};
},
}
);
because I did't create DESERIALIZE handler, the createReducer will create a default one , the following code did this job I guess.
export const withoutPersistence = reducer => {
const wrappedReducer = ( state, action ) => {
switch ( action.type ) {
case SERIALIZE:
return undefined;
case DESERIALIZE:
return getInitialState( reducer );
default:
return reducer( state, action );
}
};
wrappedReducer.hasCustomPersistence = true;
return wrappedReducer;
};
even though I can see the product at state tree in browser, after executing this line
const serverState = reducer( window.initialReduxState, { type: DESERIALIZE } );
the product is lost in serverState. so the product value I set at server site disappeared.
Calypso is a little difficult for me , I have studied it for months, it is very difficult for beginners to figure out how it works.
Ah okay, sounds like you're essentially doing all the right things.
Server de- and rehydration relies on the SERIALIZE and DESERIALIZE actions. You don't have to make your reducer listen to them explicitly however -- instead, you provide a serialization schema as the third argument to createReducer. There is a section in the 'Our Approach to Data' doc that covers this stuff.
Calypso is a little difficult for me , I have studied it for months, it is very difficult for beginners to figure out how it works.
It's certainly a large and somewhat complex codebase! Is there anything specific you're trying to get done? Maybe I can give you some pointers.
@ockham, very appreciate, I have read almost all Calypso documents, right now, I am doing some customization, the most difficult for me is debugging when have troubles, I think I need more practice.
you provide a serialization schema as the third argument to createReducer
this way works. thanks again.
Most helpful comment
Ah okay, sounds like you're essentially doing all the right things.
Server de- and rehydration relies on the
SERIALIZEandDESERIALIZEactions. You don't have to make your reducer listen to them explicitly however -- instead, you provide a serialization schema as the third argument tocreateReducer. There is a section in the 'Our Approach to Data' doc that covers this stuff.It's certainly a large and somewhat complex codebase! Is there anything specific you're trying to get done? Maybe I can give you some pointers.