パラメータをselectタグの初期値を設定する際の注意点
今回、selectタグの初期値を指定してアクセスする要件がありました。
Gatsbyでパラメータを指定してアクセスすることで今回の要件を満たそうとしたところ、課題がありましたので紹介します。
前提条件
- Gatsbyで開発
- デプロイ後発生
今回実現したい要件
- アクセス先URI:
/content/
- カテゴリーを絞り込んだ場合のアクセス先URI:
/content/?category={category_name}
アクセス先URIに応じて、パラメータの有無を参照してページの初期表示カテゴリーを設定します。
import React, { useState } from "react"
import type { PageProps } from "gatsby"
const CATEGORY = [
{ value: 'news', label: 'News'},
{ value: 'blog', label: 'Blog'}
]
const Content = ({ location }: PageProps) => {
const parameter = location.search.match(/category=(.*?)(&|$)/)
const [selected, setSelected] = useState<string>(parameter !== null ? parameter[1] : CATEGORY[0].value)
return (
<select value={selected} onChange={e => setSelected(e.target.value)}>
{
CATEGORY.map((v, i) => <option key={i} value={v.value}>{v.label}</option>)
}
</select>
)
export default Content
パラメータが存在しなければ、CATEGORY
の最初の値を初期値に設定します。
ビルド後の課題
上記の内容で、開発は完了したのですが、本番デプロイ用のファイルでは、以下問題が発生します。
以下のようなURIを直接叩くと、パラメータの値が正しく設定されない事象が発生しました。
/content/cateogry=blog
Gatsby側のルーティングで遷移する場合には正しく動きますが、直接URIにアクセスした場合にのみ、selectタグの初期値が設定されない事象が発生しました。
原因
Gatsbyはビルド後、静的HTMLファイルを生成します。対象ページの静的HTMLファイルを参照すると以下のようになっていました。
<select>
<option selected="" value="news">News</option>
<option value="blog">Blog</option>
</select>
Reactのselectタグでは、optionのselected属性は利用されず、valueに引き渡される値を一箇所で管理しているため、DOMの上書きが行われない状況が原因なのでしょうか。
Gatsbyの静的HTML生成時に自動的にselected属性が付与される仕様に難があるなぁと思ってしまいますが、、、どうなんでしょうか。
対策
静的ページとしてもパラメータを扱う以上、直接アクセスを許容するのは考慮するべき点だとは思いつつ、GatsbyとNextで今回の事象を検証してみました。
Nextではビルドのファイルにはselect属性は設定されていないようで、直接パラメータアクセス時も問題なく、値が設定されていることを確認しました。
<select>
<option value="news">News</option>
<option value="blog">Blog</option>
</select>
もう黙ってNext使うようにしましょうかね。。
パラメータを扱うにしろ、Nextの方だ断然使いやすい。
今回は、ここからフレームワークの変更は流石にできないので、レンダリング後に、DOMの更新を行うことで対応してみました。
selectタグにuseRef
を設定してuseEffect
でDOMの更新を行いました。
useEffect(() => {
const getIndex = CATEGORY.findIndex((v) => v.title === selected)
ref.current.selectedIndex = getIndex
}, [])
フレームワークの仕組みをもっと勉強しないといけないですね。。
参考資料
Discussion