Zustand store is not updated on URL change

140 Views Asked by At

I have a Zustand store connected to the URL. Look at the code below.

import { create } from "zustand";
import { persist, StateStorage, createJSONStorage } from "zustand/middleware";

const pathStorage: StateStorage = {
  getItem: (key): string => {
    const pathname = window.location.pathname;
    const p = pathname.split("/").filter(Boolean)[3];
    return p;
  },
  setItem: (key, newValue): void => {
    const slug = JSON.parse(newValue).state.tab;
    const oldSlug = window.location.pathname.split("/").filter(Boolean)[3];
    const newUrl = `/fsd/sdet/class-structure/${slug}`;
    if (oldSlug) {
      window.history.replaceState(null, "", newUrl);
    } else {
      window.history.pushState(null, "", newUrl);
    }
  },
  removeItem: (key): void => {
  },
};

export const useStore = create<{
  tab: string;
  setTab: (tab: string) => void;
}>()(
  persist(
    (set, get) => ({
      tab: "",
      setTab: (tab: string) => set({ tab }),
    }),
    {
      name: "tab-storage",
      storage: createJSONStorage(() => pathStorage),
    },
  ),
);

When I use browser back/forward buttons, the URL is changed, but store is not updated automatically.

Anyone who can help? I use Next.js 14, React 18 and Zustand 4.5.

1

There are 1 best solutions below

0
Nazrul Chowdhury On BEST ANSWER

Your implemented Zustand store is persisting its state to the URL using the persist middleware with a custom StateStorage implementation called pathStorage. This setup is designed to update the URL whenever the state changes and to retrieve the state from the URL when the page is loaded.
However, when you use the browser's back/forward buttons, the URL changes but the store is not updated automatically. you need to listen for changes in the URL and update the store accordingly. You can achieve this by adding an event listener for the popstate event, which is triggered when the user navigates through the history using back or forward buttons. You can try to listen for popstate events and update the store accordingly. Something like this,

... your imports...

const pathStorage: StateStorage = {
  getItem: (key): string => {
    const pathname = window.location.pathname
    const p = pathname.split("/").filter(Boolean)[3]
    return p
  },
  setItem: (key, newValue): void => {
    const slug = JSON.parse(newValue).state.tab
    const oldSlug = window.location.pathname.split("/").filter(Boolean)[3]
    const newUrl = `/fsd/sdet/class-structure/${slug}`
    if (oldSlug) {
      window.history.replaceState(null, "", newUrl)
    } else {
      window.history.pushState(null, "", newUrl)
    }
  },
  removeItem: (key): void => {
  },
}

export const useStore = create<{
  tab: string
  setTab: (tab: string) => void
}>()(
  persist(
    (set, get) => ({
      tab: "",
      setTab: (tab: string) => {
        set({ tab })
        pathStorage.setItem("tab", JSON.stringify({ state: { tab } }))
      },
    }),
    {
      name: "tab-storage",
      storage: createJSONStorage(() => pathStorage),
    },
  ),
)

// Add event listener for popstate event to update store when URL changes
window.addEventListener("popstate", () => {
  const tab = pathStorage.getItem("tab")
  if (tab) {
    useStore.getState().setTab(tab)
  }
})

By adding this event listener, whenever the user navigates through the browser's history using back or forward buttons, the popstate event will be triggered, and the store will be updated accordingly based on the URL.