React

Reactの「Routes,Route」の使い方で苦戦し倒した記録

React
この記事は約7分で読めます。

こんにちは,ツジです.

今回の記事はReactでFirebaseを用いた認証処理を実装しようとした際に,「Routes」や「Route」の使い方で苦戦し倒したので,その記録を残していこうと思います.

Firebaseを使った基本的なログイン機能の実装について

基本的には,こちらの記事を参考に実装の方を行なっていきました.

ただこちらの記事では現在のバージョンにはない記述がされていたりするので,状況に応じて書き換える必要があります.

詳しくは今回のこの記事には書きませんが,「useHistory → Navigate」となっていたりとReactのバージョンアップに伴った破壊的変更が行われていたりするので,エラー文と戦いつつコーディングしていきました.

苦戦した箇所について

上述した「useHistory → Navigate」といったようなものについては,使い方を見てその通りに書けばいいだけなので,そこまで苦戦することはありませんでした.

苦戦した箇所は,タイトルにもあるように「Routes」や「Route」の部分でかなりの時間を要することになりました.

実際に苦戦し倒した箇所とそのコードについて

今回自分が苦戦したのは,「ログイン情報を保持した状態では任意のコンポーネントを表示し,ログイン情報を保持していない場合にはLogin画面を表示する」というものでした.

実際に書いていたコードがこちらになります.

// Top.js
import SignUp from './components/SignUp';
import Login from './components/Login';
import ToDoApp from './ToDoApp';
import { AuthProvider } from './context/AuthContext';
import { BrowserRouter, Routes, Route } from 'react-router-dom';
import PrivateRoute from './components/PrivateRoute';
function Top() {
  return (
    <div style={{ margin: '2em' }}>
      <BrowserRouter>
        <Routes>
          <Route 
            path='/' 
            element={
              <PrivateRoute component={<ToDoApp/>}/>}
          />
          <Route path='/signup' element={<SignUp/>} />
          <Route path='/login' element={<Login/>} />
        </Routes>
      </BrowserRouter>
    </div>
  );
}
export default Top;
//PrivateRoute.js
import { Route, Navigate } from 'react-router-dom';
import { useAuthContext } from '../context/AuthContext';
const PrivateRoute = ({ component: Component, ...rest }) => {
  const { user } = useAuthContext();
  return (
    <Route
      {...rest}
      element={() => {
        return user ? 
        <Component/> : <Navigate to="/login" />;
      }}
    />
  );
};
export default PrivateRoute;

こちらのコードは一見問題がなさそうに見えるのですが,実際には次のエラーが出力されます.

A <Route> is only ever to be used as the child of <Routes> element, never rendered directly. Please wrap your <Route> in a <Routes>.

エラー内容を見る限り,<Route>は<Routes>下でしか使えない(<Route>は<Routes>の子要素としか認めない)とのことです.

...?,きちんと<Routes>内で<Route>を記載しているのに不思議ですね.

実際に自分はこのエラーで1時間近く格闘することになります.

エラーを解消できた際の実際のコードについて

色々と調べ倒して試行錯誤を重ねた結果,次のようなコードでうまく動くようになりました!

//Top.js
import SignUp from './components/SignUp';
import Login from './components/Login';
import ToDoApp from './components/ToDoApp';
import { BrowserRouter, Routes, Route } from 'react-router-dom';
import PrivateRoute from './components/PrivateRoute';
function Top() {
  return (
    <div style={{ margin: '2em' }}>
      <BrowserRouter>
        <Routes>
          <Route
            path='/'
            element={<PrivateRoute><ToDoApp /></PrivateRoute>}
          />
          <Route path='/signup' element={<SignUp />} />
          <Route path='/login' element={<Login />} />
        </Routes>
      </BrowserRouter>
    </div>
  );
}
export default Top;
// PrivateRoute.js
import {​ Navigate }​ from 'react-router-dom';
import {​ useAuthContext }​ from '../context/AuthContext';
import ToDoApp from './ToDoApp';
const PrivateRoute = () => {​
  const {​ user }​ = useAuthContext();
  return (
    user ? <ToDoApp/> : <Navigate to="/login" />
  );
}​;
export default PrivateRoute;

解決結果から考えるエラーの原因について

大きく変わったというか,書き換えた部分はPrivateRoute.jsのこの部分ですね.

  return (
    user ? <ToDoApp/> : <Navigate to="/login" />
  );

で,ここからなぜエラーが出力されていたのか考えていこうと思います.

今回書き換えた内容としては,PrivateRoute.jsのreturn文の記載にあった<Route>を消して,user情報の有無によって異なるコンポーネントを返却するような記述にしました.

書き換えた内容と出力されていたエラー分から,return文内の<Route>が<Routes>の子要素として認識されていなかったのではと考えられます.

(実際にうまく動いてくれたら原因の考察もしやすいんですが,そこまでに辿り着くのに苦労するんですよね...)

さいごに

今回はReactにおける「Routes」や「Route」で苦戦したときの記録を記事にしてみました.

「Routes」や「Route」はバージョン変更に伴って,新たに実装されたもののようで,未だに参考になりそうな記事は少ないんですよね.(解決するのにとても時間がかかってしまった理由の1つです)

ですが,今回のこの記事で自分と同じように詰まった人の助けになればいいなと思っています.

ここまで読んでいただきありがとうございました!
また,次回の記事をお楽しみに!!