🔥

Next.jsでDynamic RoutingでuseEffect / route.queryが動作しない問題

2022/08/14に公開

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が設定される。

もうちょっと詳しい説明。

https://leeyoongti.medium.com/fix-nextjs-router-query-not-working-in-useeffect-a2d9d0ac4703

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