I have an example stackblitz application using redux-state-sync that describes the problem that a window does not get initial state from other window like it should when function initStateWithPrevTab
is called.
Expected behavior (confirmed also in my old Angular app):
/
and dispatch a state change/properties/
However my linked example app works like this:
/
and dispatch a state change/properties/
The /properties/
-window receives the entire state from "main window" and it dispatches receiveIniteState
but it doesn't have any effect.
So ONLY the initial state from previous tab ("main window") is not synced to the new window (/properties/). All the state changes AFTER the /properties/ window is opened are synced without problem. The code looks like this:
store.ts
export type AppDispatch = typeof store.dispatch;
export type RootState = ReturnType<typeof store.getState>;
export const store = configureStore({
reducer: {
appProperties: appPropertiesReducer,
},
middleware: (getDefaultMiddleware) =>
getDefaultMiddleware().concat(createStateSyncMiddleware()),
});
initStateWithPrevTab(store);
AppProperties.slice.ts
interface AppPropertiesState {
propertiesId: string;
}
const initialState: AppPropertiesState = {
propertiesId: '0',
};
const propertiesSlice = createSlice({
name: 'app-properties',
initialState,
reducers: {
setPropertiesId(state, action: PayloadAction<string>) {
state.propertiesId = action.payload;
},
},
});
export const { setPropertiesId } = propertiesSlice.actions;
export const selectPropertiesId = (state: RootState) =>
state.appProperties.propertiesId;
export const getInitialState = propertiesSlice.getInitialState;
export default propertiesSlice.reducer;
index.ts
const root = createRoot(document.getElementById('app'));
root.render(
<StrictMode>
<App name="StackBlitz" />
</StrictMode>
);
App.tsx
export const App: FC<{ name: string }> = ({ name }) => {
return (
<div>
<Provider store={store}>
<BrowserRouter>
<Routes>
<Route path="/" element={<MainView />} />
<Route path="properties" element={<Properties />} />
</Routes>
</BrowserRouter>
</Provider>
</div>
);
};
MainView.tsx
let i = 0;
export function MainView() {
function onSetPropertiesId(): void {
store.dispatch(setPropertiesId((++i).toString()));
}
const propertiesId = useSelector(selectPropertiesId);
return (
<div>
<p>Click the button increment propertiesId</p>
<button onClick={onSetPropertiesId}>Increment</button>
<p>Properties Id : {propertiesId}</p>
</div>
);
}
Properties.tsx
export function Properties() {
const [content, setContent] = useState<ReactNode | undefined>(undefined);
const propertiesId = useSelector(selectPropertiesId);
useEffect(() => {
setContent(<span>PropertiesId : {propertiesId}</span>);
}, [propertiesId]);
return <div>{content}</div>;
}
How to reproduce it in the example stackblitz application:
After that syncing works. So the question is that why 2nd window doesn't init the state from previous window even though initStateWithPrevTab
is called? As mentioned before the initial state syncing in the library works and I have no problem in my old Angular app.
This can be fixed by combining reducers and moving initStateWithPrevTab to App.tsx.
store.ts
export const store = configureStore({
reducer: withReduxStateSync(
combineReducers({
appProperties: appPropertiesReducer
})
),
middleware: (getDefaultMiddleware) =>
getDefaultMiddleware().concat(createStateSyncMiddleware()),
});
App.tsx
export const App: FC<{ name: string }> = ({ name }) => {
// function call moved here
initStateWithPrevTab(store);
return (
<div>
<Provider store={store}>
<BrowserRouter>
<Routes>
<Route path="/" element={<MainView />} />
<Route path="/properties" element={<Properties />} />
</Routes>
</BrowserRouter>
</Provider>
</div>
);
};