はじめに
Vitest でテストを書いたら useNavigate() 関連のエラーに遭遇したので、原因と解決策をまとめます。
発生したエラーメッセージ
テスト実行時に以下のエラーが発生しました。
Error: useNavigate() may be used only in the context of a <Router> component.
❯ Login src/pages/Login.tsx:14:22
12|
13| export const Login = () => {
14| const navigate = useNavigate();
| ^
エラー発生時のコード
loginTest.spec.tsx
import { render, screen, waitFor } from "@testing-library/react";
import { MemoryRouter } from "react-router-dom";
import { Login } from "../pages/Login";
describe("初期表示", () => {
test("タイトルが表示できること", async () => {
render(
<MemoryRouter>
<Login />
</MemoryRouter>
);
await waitFor(() => {
const title = screen.getByTestId("testLoginTitle");
expect(title).toHaveTextContent("ログイン画面");
});
});
});
Login.tsx
import { signInWithGoogle } from "../service/auth";
import { useEffect, useState } from "react";
import { supabase } from "../utils/supabase";
//ログイン直後に使用するユーザー情報
import { User as SupabaseUser } from "@supabase/supabase-js";
import { fetchUser } from "../service/fetchUser";
import { useNavigate } from "react-router";
import { Header } from "../components/Header";
export const Login = () => {
const navigate = useNavigate();
// 認証ユーザー
const [authUser, setAuthUser] = useState<SupabaseUser | null>(null);
const [bootstrapping, setBootstrapping] = useState(true);
原因
Login.tsx では react-router から useNavigate を import していました。
一方、テストでは react-router-dom の MemoryRouter を使用していたため、異なる Router コンテキストを参照してしまいエラーが発生していました。
解決策
Login.tsxの import を修正するだけで解決しました。
Login.tsx
import { signInWithGoogle } from "../service/auth";
import { useEffect, useState } from "react";
import { supabase } from "../utils/supabase";
//ログイン直後に使用するユーザー情報
import { User as SupabaseUser } from "@supabase/supabase-js";
import { fetchUser } from "../service/fetchUser";
- import { useNavigate } from "react-router";
+ import { useNavigate } from "react-router-dom";
import { Header } from "../components/Header";
おわりに
エラーログを読んでいても「import が原因」とまでは気づけませんでした。
今回の学びとして、エラーの最初の1行目 と スタックトレースに最初に出てくる自作ファイル を確認すれば、原因に直結することが分かりました。
また、react-router-dom は内部で react-router をラップしているため、Webアプリ開発では基本的に react-router-dom を使うべきだと理解できました。
参考