【JavaScript】【React】React Router v6でクエリ文字列を取得・変更する

JavaScript,React,React Router

はじめに

React Router v6を使っていて、クエリ文字列(?以降の文字列)を取得する必要があったため、少し調べてみました。そのメモです。

useSearchParams()

React Router v6でクエリ文字列を取得・変更するには、useSearchParams()フックを使います。使い方はuseState()に似ています。

const [searchParams, setSearchParams] = useSearchParams();

searchParamsにはURLSearchParamsオブジェクトが入ります。setSearchParams()を使うことでクエリ文字列を変更できます。

URLSearchParamsオブジェクト

URLSearchParamsオブジェクトは、React Routerの一部ではなく、Web APIの一部です。以下のようなメソッドが用意されています。

メソッド機能
append(name, value)name=valueを追加します。
delete(name)nameと関連付けられた値を削除します。
entries()名前と値の組を表すイテレータを返します。
forEach(callback)各名前と値の組に対してコールバック関数を呼び出します。
get(name)nameに対応する値を取得します。
getAll(name)nameに対応する値をすべて取得します。
has(name)nameが存在するかtrue/falseで返します。
keys()名前を表すイテレータを返します。
set(name, value)name=valueをセットします。すでにnameがある場合は置き換えます。
sort()nameで昇順に並べ替えます。
toString()オブジェクトの内容をクエリ文字列にして返します。
values()値を表すイテレータを返します。

使用例

(1)クエリ文字列を取得する

クエリ文字列を取得するには、get()またはgetAll()を使います。
この例では、リンクに直接埋め込まれたname1, name2という名前を持つ値を取り出して表示します。name1については対応するすべての値を表示します。

import { React, useState } from "react";
import ReactDOM from "react-dom";
import {
    BrowserRouter,
    Route,
    Routes,
    Link,
    useSearchParams,
} from "react-router-dom";

const App = () => {
    return (
        <Routes>
            <Route path="/" element={<TopPage />} />
            <Route path="/test1" element={<Test1 />} />
            <Route path="/test2" element={<Test2 />} />
        </Routes>
    );
};

if (document.getElementById("app")) {
    ReactDOM.render(
        <BrowserRouter>
            <App />
        </BrowserRouter>,
        document.getElementById("app")
    );
}

const TopPage = () => {
    return (
        <div>
            <Link
                to="test1?name1=value11&name1=value12&name2=value2"
            >
                テスト1
            </Link>
            /
            <Link to="test2">
                テスト2
            </Link>
        </div>
    );
};

const Test1 = () => {
    const [searchParams] = useSearchParams();

    return (
        <ul>
            <li>name1 : {searchParams.getAll("name1").join()}</li>
            <li>name2 : {searchParams.get("name2")}</li>
        </ul>
    );
};

(2)クエリ文字列を変更する

クエリ文字列を変更するには、useSearchParams()の2番目の戻り値(ここではsetSearchParams()を使用します)。setSearchParams()に渡す値の形式は、この例のような{ key: value }という組を持つオブジェクトのほかに、いくつかあるようです。

この例のコードは上の続きになっています。
フォームから入力されたkeyとvalueの組をクエリ文字列に付加すると同時に、リストとしてフォームの下側に表示します。

const Test2 = () => {
    const [searchParams, setSearchParams] = useSearchParams();
    const [key, setKey] = useState("");
    const [value, setValue] = useState("");
    const [query, setQuery] = useState({});

    const handleKeyChange = (e) => {
        setKey(e.target.value);
    };

    const handleValueChange = (e) => {
        setValue(e.target.value);
    };

    const handleAppend = (e) => {
        if (key === "" || value === "") {
            return;
        }

        let q = { [key]: value, ...query };
        setQuery(q);
        setSearchParams(q);
    };

    const queries = [];
    for (const [name, val] of searchParams.entries()) {
        queries.push(name + ": " + val);
    }

    return (
        <div>
            key <input type="text" onChange={handleKeyChange} />
            value <input type="text" onChange={handleValueChange} />
            <button onClick={handleAppend}>追加</button>
            <hr />
            <ul>
                {queries.map((s) => {
                    return <li key={s}>{s}</li>;
                })}
            </ul>
        </div>
    );
};

React Router v5以前では

React Router v5以前では、useSearchParams()がありません。代わりにuseLocation()URLSearchParamsオブジェクトを使うことで、似たようなことが可能です。

const location = useLocation();
const searchParams = new URLSearchParams(location.search);

まとめ

React Router v6でクエリ文字列を取得・変更できるフックである、useSearchParams()について調べてみました。React Router v6でないと使えないので注意が必要ですが、クエリ文字列の取得・変更が簡単に行えます。
それ以前のバージョンではuseLocation()URLSearchParamsオブジェクトを使うことで似たようなことが可能です。