いまいる場所を調べる
いま開いているページのリンクに印を付けたり、次のページを読み込んでいることを見せたりするには、ブラウザがいまどのパスにいるかを調べます。このページでは、そのためのフックと関数を紹介します。
このページの内容
いまのパスを読む
usePathnameは、ブラウザがいま開いているパスを返します。末尾のスラッシュは落とした形で返ります。
src/current-path.tsximport { usePathname } from '@k8ordo/router';
export function CurrentPath() {
const pathname = usePathname();
return <p>You are at {pathname}</p>;
}再描画されるのはパスが変わったときだけで、クエリ文字列が変わっても再描画されません。クエリ文字列を返さないのは、その値を読むのが@k8ordo/stateの役割だからです。@k8ordo/stateでは、コンポーネントが読んでいる値が変わったときだけ再描画されます。
パスはURLでの書き方のままで、デコードはしません。日本語のような文字は、パーセントエンコードされた形で返ります。
usePathnameはルート表ではなくブラウザのURLを読むので、@k8ordo/staticや@k8ordo/serverの下でも同じように使えます。
あるページを開いているか調べる
useMatchは、いまのパスがパターンに合えばそのparamsを、合わなければnullを返します。ナビゲーションのリンクに印を付けるときは、これを使います。
src/products-link.tsximport { href, useMatch } from '@k8ordo/router';
export function ProductsLink() {
const isCurrent = useMatch('/products') !== null;
return (
<a
aria-current={isCurrent ? 'page' : undefined}
href={href('/products')}
>
Products
</a>
);
}リンクが選ばれているかどうかは、リンクに渡すpropsではなく、こうして尋ねて決めます。<Link>のようなコンポーネントが無いので、印の付け方も自分で選べます。
あるまとまりの下にいるか調べる
パターンの後ろに/*を付けると、「そのパターンより下のどこか」という意味になります。サイドバーのように、どのまとまりを開いているかを知りたいときに使います。
matchPath('/products/*', '/products/42'); // {}
matchPath('/products/*', '/products'); // null
matchPath('/products/*', '/products', { inclusive: true }); // {}matchPathは、useMatchと同じ判定を、手元にあるパスに対して行う関数です。上の例では、判定の違いが見えるようにこちらを使っています。
/products/*は、/productsそのものには合いません。まとまりの入口のページでも印を付けたいときは、{ inclusive: true }を渡します。
/*を付けられるのは、ルート表にあるパターンの後ろだけです。Registerを登録していれば、表に無いパターンは型エラーになります。
useMatchはusePathnameの上に作られているので、再描画されるのはパスが変わったときだけです。ルート表も要らないので、フレームワークの下でも使えます。このサイトのヘッダーも、パッケージのランディングとその下のページのどちらでもパッケージ名に印が付くよう、inclusiveを付けて判定しています。
パターンとパスの照合を試す
このサイトも@k8ordo/routerの上で動いています。最初の値は、このページでusePathnameを呼んで読んだパスです。書き換えると、matchPathの結果がその場で変わります。
usePathname()の値/ja/router/location
- 呼び出し
matchPath('/:locale/router/*', '/ja/router/location')- 結果
{"locale":"ja"}
試してみる
- 最初は、パターンが
/:locale/router/*で、パスがこのページのパスです。結果は{"locale":"ja"}で、/*が受けた部分はparamsに入りません。 - パスを
/ja/routerに書き換えると、結果はnullになります。/*は、パターン自身のページには合わないからです。 - 「inclusive」をオンにすると、結果が
{"locale":"ja"}に戻ります。 - パスの末尾に
/を足しても、結果は変わりません。比べる前に、末尾のスラッシュを落とすからです。
読み込み中のページを知る
ページを移るとき、URLが先に書き換わり、新しいページは準備ができてから画面に出ます。そのため遅いナビゲーションでは、usePathnameがもう新しいパスを返しているのに、画面には前のページが残っています。
usePendingPathnameは、読み込み中のページのパスを返します。何も読み込んでいないときはnullです。
src/progress.tsximport { usePendingPathname } from '@k8ordo/router';
export function Progress() {
const pending = usePendingPathname();
if (pending === null) return null;
return <p role="status">Loading {pending}…</p>;
}値はナビゲーションが始まった時点で入り、新しいページが画面に出たときにnullに戻ります。ナビゲーションが中断されたときや、読み込みに失敗したときもnullに戻ります。
クエリ文字列だけを変える状態の更新は、ページの切り替えではないので値が入りません。ただし、フレームワークのページがクエリ文字列を読んでいて、その場で読み込み直すときは、ほかの読み込みと同じように値が入ります。
ページのparamを読む
<Router>で描くページは、useParamsに自分のパターンを渡してparamを読みます。返る値の型はパターンの文字列から決まり、値はいつも文字列です。
src/pages/product-page.tsximport { useParams } from '@k8ordo/router';
export function ProductPage() {
const { id } = useParams('/products/:id');
return <h1>Product {id}</h1>;
}渡すパターンは、「このコンポーネントはこのパターンのページとして描かれる」という宣言でもあります。別のパターンのページの中で描かれると、形の違うparamsを返す代わりに、次の例外を投げます。
useParams("/products/:id") rendered under "/products"いくつものページで使い回すコンポーネントなら、useRouteで、いま選ばれているパターンとparamsを型の無い形で受け取れます。
const { pattern, params } = useRoute();落とし穴
useParamsとuseRouteは、<Router>が持つ照合の結果を読みます。@k8ordo/staticや@k8ordo/serverの下ではブラウザにルート表が無いので、どちらも使えません。ページはparamsをpropsで受け取ります。