reactjsreduxnext.js

React component not updated with new Redux state


My problem is that a component doesn't seem to receive the proper redux state (in time?) and therefore doesn't display the sidebar component in this redux state.

The called page looks like this:

const MyLandingPage: NextPage = () => {
  const { setLayout } = useSetLayout(); // the Redux dispatch method, see below
  ...
  useEffect(
    () => {
      setLayout({
        sidebar: {
          content: <FeedSidebar filters={filters} setFilters={setNewFilters} />
      });
    },
    [...]
  );
  return <MyComponent ... />
}

This page will be embedded in the side column of my Layout.tsx:

export const Layout: React.FC = () => {
  const sidebar = useSidebarContentSelector(); // the Redux selection method, see below
  console.log('sidebar in Layout:', sidebar);
  return <SomeOtherComponents>{sidebar?.content}<SomeOtherComponents>;
}

The Redux-related hooks for the sidebar state looks like this:

export const useSidebarContentSelector = () => useSelector((state: RootState) => state.sidebarContent);

export const useSetLayout = () => {
  const { setSidebar } = sidebarContentSlice.actions;
  const dispatch = useAppDispatch();
  return {
    setLayout: (commands: { sidebar?: SidebarState) => {
      console.log('setting sidebar:', commands.sidebar);
      dispatch(setSidebar(commands.sidebar));
    }
  }  
}

The logs reads like:

sidebar in Layout: null
setting sidebar: Object { content: {…} }
sidebar in Layout: null

useSidebarContentSelector and useSidebarContentDispatch work on other occasions, I'm rather sure they are alright. But for some reason the sidebar in Layout.tsx doesn't receive the update and I figure just keeps displaying the state from the SSR.

I'm grateful for any ideas why the updated sidebar isn't received despite being dispatched and the Layout component rerendering!


Solution

  • Found it. I wasn't using a Redux store singleton, so with every rerender a new store was created. One solution might be to create the store with const store => useMemo(() => configureStore(...), []), but for my design it was more straight-forward to go with

    const isServerSide = typeof window === 'undefined';
    
    const clientSideReduxStore = configureStore(...);
    
    export const ReduxProvider: React.FC<
      Omit<ProviderProps, 'store'> & { preloadedState?: Partial<RootState> }
    > = (props) => {
      return isServerSide ? Provider({store: configureStore(...)) : clientSideReduxStore
    }
    
    

    Note that the on SSR a new store must be created at any call, so I mustn't return clientSideReduxStore in case I'm at the server-side.