Reactでクエリパラメータを使ってみる
何故この記事を書こうと思ったのか
ReactではuseStateを使ったり、Reduxを使ったりと様々な値を保持したりする機能がありますが、その中でも使用頻度がとても高いクエリパラメータ,
SubmitEventなんかで値を取得する際などで使用しますね。ページ遷移後にリロードしてもクエリパラメータは変更されないため、情報を保持しておく場面では特に多用します。
意外と解説してくれてる記事とか少ない気もする
フロントエンドエンジニアとしてはとりあえず、必要な知見となるので今回書かせてもらいます。
単純にURLを取得したいだけだったり、クエリパラメータだけ取得したい場合なんかで違うため、まとめます。
後半部分で実際にクエリの取得だったり、動的に作成したりっていう解説があります。前半は、基礎内容です、。
まずURLの仕組みについて学ぶ
そもそもURLとは、「インターネット上に存在する情報資源の、場所を指し示す技術方式」のことです。
ここで今更書くほどでもないのですが、(このURLは、自分の前回更新した記事のURLです)
https://qiita.com/nuhaha_2023/items/dc4cc6b24da04c13302a
httpsとhttpについて。
http (Hyper Text Transfer Protocol)
→webサイトを環境に依らずに問題無く表示するための通信規格(プロトコル)
この環境っていうのは、windowsだったり、macだったり、GoogleChromeだったり、Microsoft edgeだったりと、人によってブラウザとかのことを指しています。
https(Hypertext Transfer Protocol Secure)
→webサイトを環境に依らずに問題なく表示するための通信規格(プロトコル) + 暗号化通信によりセキュリティを高めた版
通信経路での第三者による情報盗聴だったり改ざんのリスクを防止できる。
最近はhttps〜のwebサイトが増えましたね。ChatGPTに比率を聞いてみました。
HTTPSとHTTPの使用割合は以下の通りです。
HTTPS: 約86%のウェブサイトがデフォルトでHTTPSを使用しています (W3Techs)。
HTTP: 残りの約14%のウェブサイトが依然としてHTTPを使用しています。
その昔、webサイトというのはHTMLをそのまま表示させるだけの静的なページが多かったんですよね。
今と比べるとデータベースに接続して情報を取得したりっていう頻度が極端に少なかったため、その名残がhttpのようです。
なので、webサイトを開設する際は盗聴改ざんリスクも防止可能な、後発のhttpsを使用するようにしましょう。
ドメインって?
https://qiita.com/nuhaha_2023/items/dc4cc6b24da04c13302a
インターネットの住所を表す特に重要な部分が、「ドメイン名」です。
ドメインには役割があり、ユーザーへの識別や、企業or個人のブランドとしての信頼性を高めるなどがあります。
上記のURLが指し示す中での「qiita.com」がドメイン名になります。
「qiita」のことを「サードレベルドメイン)
このURLには含まれていませんがよく見かける、「co」や「ne」のことを「セカンドレベルドメイン」
「.com」のことを「トップレベルドメイン」と言います。
基本的な構造を理解する!とういうことでまずURLのことをまとめてみました!事項よりReactで実際にURLからクエリパラメータを取得したり、してみたいと思います!
クエリパラメータの特徴
ローカルで個人的に作成しているアプリでのURLを参照します。
http://localhost:5173/list/mui_form?gender=woman&selectedCities=1
このURLの?以降に書かれている内容がクエリパラメータになります。
ここに書き込まれている値は、useStateやReduxの状態保持なんかとは違い、リロードが行われた後も値が保持され続けます。
ここから実際にURLを取得します!!
vite環境で実際に、URLを取得したりしてみましょう。 ローカルホストからの取得となります。
実際のサンプルコードと画面になります。(不要でしたら飛ばしてください)
const ListMuiFormLayout: FC = () => {
const [searchParams, setSearchParams] = useSearchParams();
const { input: title, setInput: setTitle, onChangeInput: handleTitleChange } = useInput();
const [gender, setGender] = useState(GENDER_RADIO_ARR[0].value);
const [cities, setCities] = useState(CITIES_CHECK_ARR);
const handleGenderChange = useCallback(
(e: ChangeEvent<HTMLInputElement>) => {
setGender(e.target.value);
},
[gender],
);
const handleCityChange = useCallback(
(value: number) => {
const updatedCities = cities.map((city) => {
if (city.value === value) return { ...city, checked: !city.checked };
return city;
});
setCities(updatedCities);
},
[cities],
);
const handleSubmit = (e: FormEvent<HTMLFormElement>) => {
e.preventDefault();
const selectedCities = cities.filter((city) => city.checked).map((city) => city.value);
const queryParams: Record<string, string> = {};
if (title) queryParams.title = title as string;
if (gender) queryParams.gender = gender;
if (selectedCities.length > 0) queryParams.selectedCities = selectedCities.join(",");
setSearchParams(queryParams);
};
useEffect(() => {
const queryTitle = searchParams.get("title");
const queryGender = searchParams.get("gender");
const querySelectedCities = searchParams.get("selectedCities");
if (queryTitle) setTitle(queryTitle);
if (queryGender) setGender(queryGender);
if (querySelectedCities) {
const selectedCityArray = strNumArrToNumConvert(querySelectedCities.split(","));
const updatedCities = CITIES_CHECK_ARR.map((city) => {
if (selectedCityArray.includes(city.value)) return { ...city, checked: true };
return city;
});
setCities(updatedCities);
}
}, [searchParams]);
return (
<div>
<Header />
<CContainer>
<h2>muiで簡単なフォームを作成する。決定した値をクエリパラメータで保持。リロード時に値を再セットする。</h2>
<form onSubmit={handleSubmit}>
<p>名前</p>
<CTextField value={title} onChange={handleTitleChange} />
<div style={{ display: "flex", flexDirection: "column" }}>
<FormControl>
<FormLabel>性別選択</FormLabel>
<RadioGroup aria-label="gender" name="gender1" value={gender} onChange={handleGenderChange} row>
{GENDER_RADIO_ARR.map((item, index) => (
<CRadioButton key={index} label={item.label} value={item.value} />
))}
</RadioGroup>
</FormControl>
<FormControl>
<FormLabel>行ってみたい都市</FormLabel>
<FormGroup row>
{cities.map((city, index) => (
<CCheckBox key={index} label={city.label} value={city.value} checked={city.checked} onChange={() => handleCityChange(city.value)} />
))}
</FormGroup>
</FormControl>
</div>
<CContainedButton type="submit">決定</CContainedButton>
</form>
</CContainer>
</div>
);
};
export default ListMuiFormLayout;
まずはシンプルにURLに関する情報を取得する方法。
const url = useLocation();
console.log(url)
ログ出力の結果から分かるようにuseLocationのオブジェクトが取得できました。
出力されているキー名を指定していけばpathだったりが取得できるわけですね。
多用するのはやはり、「pathname」と「search」でしょう。
実際にクエリを取得して表示してみる。
const url = useLocation();
console.log(url.search)d
// 結果 => ?gender=woman&selectedCities=1
ただのstringが帰ってくるだけです。
実際にこの値を使用するには....?
まずはコード上にて
const [searchParams, setSearchParams] = useSearchParams();
上記を書きましょう。
このコードは useSearchParams というカスタムフックが返す配列を分割代入で受け取っています。具体的には、useSearchParams が返す配列の1番目の要素を searchParams という変数に、2番目の要素を setSearchParams という変数に代入しています。
とりあえず、書きます。順番を間違えてはいけません。
そして以下のコードを記入することで実際に値を取得可能です。
// URLはこちらの想定です。
http://localhost:5173/list/mui_form?gender=woman&selectedCities=1
const [searchParams, setSearchParams] = useSearchParams();
useEffect(() => {
const queryTitle = searchParams.get("title"); //存在しないパラメータ名のため、「null」を返す
const queryGender = searchParams.get("gender");// 存在するため、文字列「"woman"」を返す
const querySelectedCities = searchParams.get("selectedCities"); //こちらも存在するため、文字列"1"を返す
if (queryTitle) setTitle(queryTitle); // queryTitleはnull なのでif文は発生しない
if (queryGender) setGender(queryGender); //queryGenderは"woman"のためif文が発生する
.... //続く
}, [searchParams]);
お分かりいただけたかと思うのですが、以下のような特徴があります。
①該当のないキー名を指定すると「null」が返却される
②数値を取得した場合であっても、文字列となってしまう。
なので②に関しては、実際に使用する前に文字列から数値への変換を行うような関数が必要となりますので覚えておきましょう。
ちなみにクエリパラメータの取得に関する方法は2パターンあります
//①
const [searchParams, setSearchParams] = useSearchParams();
//②
URLSearchParams(useLocation().search)
上記の解説記事が多いので、ちょっと今まで理解に苦しんでいたのでここで回答します。
結論を言うと、①が新しい書き方、②は古い書き方のようです。
useSearchParams はReact Router v6で導入されたフックで、Reactのコンポーネント内でクエリパラメータを簡単に操作できるように設計されています。このフックは、ステートのように扱えるので、直感的でReactの哲学に沿った方法です。
利点:
シンプルなAPI: useSearchParams は直感的で使いやすい。
状態管理と統合: Reactの状態管理フローと自然に統合される。
React Routerと連携: React Routerの他の機能やフックと一貫性がある。
URLSearchParams(useLocation().search)
URLSearchParams を useLocation フックと組み合わせて使う方法は、React Router v6以前から使われている伝統的な方法です。こちらも有効ですが、最新のReact Routerの機能を最大限に活用するためには、useSearchParamsの方が適しています。
利点:
柔軟性: ネイティブのURLSearchParams APIに直接アクセスできるため、より細かい操作が可能。
従来の方法: React Router v5以前からの互換性がある。
とりあえず僕は①のカスタムフックを使用していきたいかなぁと思ってます。何事も新しい方がいいことがありそうなので。。
感想
今回はURLについてとクエリパラメータの取得について書かせていただきましたが、サンプルコードの中でformの実装などが書いてあります。
少しでも参考になればいいなと思います。