Open1

SolidRouterで、queryを含めたパス変更でもpageをremountする

yunayuna

課題

solidrouterのデフォルトの設定では、
https://xxx.com/xxx?id=123
から、
https://xxx.com/xxx?id=456
に遷移した場合、自動的にマウントが再実行されず、画面が切り替わりませんが、
実際、ある記事ページから別の記事ページへの遷移のように、URLのqueryのみが変更するケースはよくあります。

これに対応するための方法です。

例えばNuxt3では
<NuxtPage :key="$route.fullPath" /> のようにページのkeyを設定し、そのkeyが変更されたらマウントされる(画面が切り替わる)という動作が実現できます。

Solid.jsでの解決方法

結論から言うと、NuxtのようにRouter側での実装は現在はできませんでした。
そこで、queryの変更でも画面を切り替えたいページ毎に、queryの変更によって画面がリレンダリングされる仕組みを組み込むことで解決しました。

基本的な実現方法

例えばこんなページがあったら、

Sample.tsx
export default function SamplePage() {
  return (
    <div>
       xxxx
    </div>
  )
}

このように、Wrapperをかまして<Show> にkeyed attributeを指定することで、
whenに指定した値が変更される度に、マウント処理が走るようになります。
これで、画面の切り替えを発生させられます。

Sample.tsx
import { useLocation } from "@solidjs/router";

function SamplePage() {
  return (
    <div>
       xxxx
    </div>
  )
}

export default function SamplePageWrapper() {
const routeLocation = useLocation();
  return (
  <Show when={routeLocation.search} keyed>
    <SamplePage />
  </Show>
  )
}

このwrapperを汎用的なコンポーネントに切り出したのがこちら。

RouteReloader.tsx
import { Show, JSX } from "solid-js";
import { useLocation } from "@solidjs/router";

// URLクエリが変わるたびにコンポーネントを再マウントする汎用コンポーネント
interface RouteReloaderProps {
  children: JSX.Element
}

export function RouteReloader(props: RouteReloaderProps) {
  const location = useLocation();
  
  return (
    // Showのkeyedを使うことで、when条件が変わるたびに子要素を完全に再マウント
    <Show when={location.search} keyed>
      {props.children}
    </Show>
  );
}

上記を使って、Wrapperを書く。

export default function SamplePageWrapper() {
  return (
    <RouteReloader>
      <SamplePage />
    </RouteReloader>
  );
}

※注意RouteReloaderを、直接書くとうまく動きません。
 これはSamplePageのmountが再実行されるのではなく、単純に内部の情報がリレンダリングされるだけだからだと思います。

import { useLocation } from "@solidjs/router";

function SamplePage() {
  onMount(() =>
  //onMountは再実行されないので、queryによって内部の情報が変化するページでも、リレンダリングされない。
  xxx
  );
  return (
  <RouteReloader>
    <div>
       xxxx
    </div>
  </RouteReloader>
  )
}