😃

[Next.js] URLで状態を管理したい

2023/10/11に公開

はじめに

URLで状態を管理したいときuseRouterrouter.push()などのメソッドを使った方法があると思いますが,
今回はnext-usequerystateというライブラリを見つけたので実際に使ってみたいと思います。
https://github.com/47ng/next-usequerystate

URLに状態をもたすメリット

URLに状態をもたすメリットとして大きく2つあると考えています。

データの永続化

画面をリロードした際useStateで管理している状態の場合消えてしまいますが、URLに状態をもたすことでURLに変更を加えない限り状態を残すことができます。

状態を持たせたまま他人に共有

例えば、モーダルを開いた状態の画面をそのまま他人に共有することなどもできます。

実装

hookなので、useStateなどと同じように使用することができます。
useQueryState()の第一引数がkeyになります。

データの永続化の例

(コードは一部簡略化してます)

import { useQueryState } from "next-usequerystate";

export default function Page() {
  const [name, setName] = useQueryState("name");
  
  return (
    <div className="min-h-screen flex items-start pl-28 bg-gray-50">
      <div className="p-8 shadow-lg rounded-lg bg-white max-w-md w-full">
        <form>
          <div className="mb-4">
            <label htmlFor="name" className="block text-sm font-medium mb-2">
              Name
            </label>
            <input
              type="text"
              value={name || ""}
              onChange={(e) => setName(e.target.value)}
              id="name"
              name="name"
              placeholder="Your name"
              autoComplete="off"
              className="w-full p-2 border rounded-md focus:outline-none focus:ring-2 focus:ring-blue-400 focus:ring-opacity-50 transition duration-300"
            />
          </div>
          <div>
            <button
              type="submit"
              className="bg-blue-500 text-white p-2 rounded mr-5 hover:bg-blue-600"
            >
              Submit
            </button>
          </div>
        </form>
      </div>
    </div>
  );
 }

以下のようにリアルタイムに状態がURLに反映され、リロードしても状態は保存されます。

状態を持たせたまま他人に共有の例

状態がstring型でない場合は第2引数のオブジェクトにパース関数を渡す必要があります。
(コードは一部簡略化してます)

import { useQueryState, parseAsBoolean } from "next-usequerystate";

export default function Page() {
  const [isModalOpen, setIsModalOpen] = useQueryState(
    "isModalOpen",
    parseAsBoolean
  );
  
  return (
    <div className="min-h-screen flex items-start pl-28 bg-gray-50">
      <div className="p-8 shadow-lg rounded-lg bg-white max-w-md w-full">
        <form>
          <div>
            <button
              type="button"
              onClick={() => setIsModalOpen(true)}
              className="bg-green-500 text-white p-2 rounded hover:bg-green-600"
            >
              Open Modal
            </button>
          </div>
        </form>
      </div>
      
      {isModalOpen && (
        <div className="fixed top-0 left-0 w-full h-full flex items-center justify-center bg-black bg-opacity-50">
          <div className="bg-white p-8 rounded-lg">
            <h2 className="text-xl mb-4">Modal Content</h2>
            <p>Here is the content of the modal.</p>
            <button
              onClick={() => setIsModalOpen((old) => !old)}
              className="mt-4 bg-red-500 text-white p-2 rounded hover:bg-red-600"
            >
              Close Modal
            </button>
          </div>
        </div>
      )}
    </div>
  );
}

(画質が悪いですが。。。)以下のようにモーダルを開いた状態のリンクを共有することができます。

おわり

今回は"next-usequerystate"を簡単に触ってみました。
今回例に出したstring型やboolean型以外にも様々な型に対応してるみたいなので、引き続き色々なパターンを試していこうと思います!

Discussion