🔥
Next.jsでDynamic RoutingでuseEffect / route.queryが動作しない問題
Dynamic routingは"pages/[article].js"みたいな指定をすると"pages/cat.js", "pages/dog.js"みたいなページを1つのjsxファイルで扱える機能。SSRなどでお馴染み。
今回はタブの切り替えで"setting/profile.js"と"setting/billing.js"を扱うページを作ってみた。ページ名は"setting/[tab].js"
export default function Setting() {
const router = useRouter();
const [tab, setTab] = useState("profile");
useEffect(() => {
if (!router.isReady) return;
setTab(router.query.tab)
}, [router.isReady])
return (
<>
{
tab == "profile" ? <Profile></Profile> : <Billing></Billing>
}
</>
)
}
これは動かない。簡単にいえばrouter.query.tabが正しく設定されるまでラグがあるから。
useEffectを使うのはrouterがページをロードしてからhydrationが終わるまで時間を食うから。つまりページをロードした瞬間はrouter.isReadyはfalseになってる。そこでisReadyを使ってrouterの完了を待つ。useEffectの関数の後の配列に入れてある変数が変化するとuseEffectが発火する。router.isReadyがtrueになるとuseEffectが発火してtabが設定される。
もうちょっと詳しい説明。
Pages that are statically optimized by Automatic Static Optimization will be hydrated without their route parameters provided, i.e query will be an empty object ({}).
After hydration, Next.js will trigger an update to your application to provide the route parameters in the query object.
だけどそれでも上のコードは動かない。
(ここから下は調査中なので鵜呑みにしないでください)
おそらくrouter.isReadyの更新タイミングとrouter.queryの更新タイミングが微妙にずれている。つまりisReadyがtrueになってもまだrouter.queryが更新されてない。そこでuseEffectの監視対象にrouter.queryを含めるとうまくいく。
export default function Setting() {
const router = useRouter();
const [tab, setTab] = useState("profile");
useEffect(() => {
if (!router.isReady) return;
setTab(router.query.tab)
}, [router.isReady, router.query.tab])
return (
<>
{
tab == "profile" ? <Profile></Profile> : <Billing></Billing>
}
</>
)
}
Discussion