🔖

50行で作るSPA & Global state

2024/08/09に公開

History APIuseSyncExternalStoreを勉強する目的で、SPAとGlobal stateを自作した
History APIのstateにグローバルステートを保存するため、ブラウザバックなどをしてもステートが保持される

state.ts

import { MouseEventHandler, useSyncExternalStore } from "react";

export const useGlobalState = () =>
  useSyncExternalStore(subscribeState, getGlobalStateSnapshot);

export const useSearch = () =>
  useSyncExternalStore(subscribeState, getSearchSnapshot);

export const handleLink: MouseEventHandler<HTMLAnchorElement> = (event) => {
  event.preventDefault();
  pushURL(event.currentTarget.href);
};

export const pushURL = (url: string) => {
  history.pushState(history.state, "", url);
  eventTarget.dispatchEvent(new CustomEvent("change"));
};

export const replaceGlobalState = (
  setGlobalStateAction: (
    prevGlobalState: GlobalState | null
  ) => GlobalState | null
) => {
  history.replaceState(setGlobalStateAction(history.state), "");
  eventTarget.dispatchEvent(new CustomEvent("change"));
};

const eventTarget = new EventTarget();

addEventListener("popstate", () => {
  eventTarget.dispatchEvent(new CustomEvent("change"));
});

const subscribeState = (callback: () => void) => {
  const handleChange = () => {
    callback();
  };
  eventTarget.addEventListener("change", handleChange);

  return () => {
    eventTarget.removeEventListener("change", handleChange);
  };
};

const getGlobalStateSnapshot = (): GlobalState | null => history.state;
const getSearchSnapshot = () => location.search;

globalState.d.ts

interface GlobalState {
  /* ここにグローバルステートの型定義を書いていく */
}

写真地図という個人開発アプリをこれで動かしてる
Image from Gyazo

Discussion