1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

お題は不問!Qiita Engineer Festa 2024で記事投稿!
Qiita Engineer Festa20242024年7月17日まで開催中!

さまざまな表示を一つのパスでルーティングするReact Routerの活用

Posted at

はじめに

React Routerは簡単にクライアントサイドルーティングを実現できるライブラリです。

業務で使用しているときに、同じパスでも条件によって表示させるコンポーネントを変えたい、という処理が発生したので、どのように対応したのかをまとめていきます。

環境

以下のものを使っています。

ライブラリ バージョン
React 18.2.0
TypeScript 5.2.2
Vite 5.2.0
ReactRouter 6.23.1

古い分岐処理の書き方

現場で対応したときはRouteコンポーネントのrenderに対して、条件分岐を書いてコンポーネントを設定していましたが、あまりそれはよろしくない書き方だったようです。

よくない書き方
<Route
  path="/dashboard"
  render={() => {
    const userRole = checkUserRole();
    // ここで条件分岐を書いている
    return <>{userRole === "admin" ? <AdminView /> : <UserView />}</>;
  }}
/>;

renderの中には何でもかけるんですが、処理が複雑になる可能性も考えると、コンポーネントとして切り出したほうがよさそうです。

新しい分岐処理の書き方

新しいバージョン(ReactRouterのv6から)ではRouteコンポーネントは使わず、RouteObjectcreateBrowserRouterでルーティングの情報を設定します。

Routeコンポーネントを使わないと`render`が使えないのでどのように分岐させるか、というところですが、そのような分岐はラッパーのコンポーネントを作成して対応します。

メインの処理

今回はクエリパラメータの有無によって表示するコンポーネントを分岐させるようにします。
まず、メインの処理は以下のようになります。

main.tsx
import React from "react";
import ReactDOM from "react-dom/client";
import {
  RouteObject,
  RouterProvider,
  createBrowserRouter,
  useSearchParams,
} from "react-router-dom";
import App from "./App";
import Child from "./Child";
import OtherChild from "./OtherChild";

// ラッパーコンポーネント
const WrapperChild = () => {
  const [searchPrams] = useSearchParams();
  const param = searchPrams.get("param");
  return param ? <Child /> : <OtherChild />;
};

// ルーティングの定義
const routes: RouteObject[] = [
  { path: "/", element: <App /> },
  { path: "/child", element: <WrapperChild /> },
];

// ルーターの生成
const router = createBrowserRouter(routes);

ReactDOM.createRoot(document.getElementById("root")!).render(
  <React.StrictMode>
    <RouterProvider router={router} />
  </React.StrictMode>
);

ルーティングの定義

/childのパスに対してクエリパラメータがくっついてくる想定です。
elementプロパティに対して、分岐処理を記載したコンポーネントを設定します。
これにより、ルーティングの定義自体がごちゃごちゃしなくなります。

ラッパーコンポーネント

分岐処理を書いたコンポーネント内ではuseSearchPramsを使ってクエリパラメータを取得しています。
これもReactRouterで用意しているメソッドになります。

取得したsearchParamsから取得したいパラメータをgetメソッドで取得します。
取得できなかった場合にはエラーにならず、nullが設定されます。

そしてpramの値によって返すコンポーネントを分岐させています。

以上がメインの主な処理になります。

その他コンポーネント

直接分岐処理に関わるわけではないので、詳細は以下から確認してください。

その他コンポーネントを確認する
App.tsx
import { Link } from "react-router-dom";

const App = () => {
  return (
    <>
      <ul>
        <li>
          <Link to="/child">パラメータなし</Link>
        </li>
        <li>
          <Link to="/child?param=admin">アドミン</Link>
        </li>
        <li>
          <Link to="/child?param=member">メンバー</Link>
        </li>
      </ul>
    </>
  );
};

export default App;
Child.tsx
const Child = () => {
  return <h1>Child Component</h1>;
};

export default Child;
const OtherChild = () => {
  return <h1>OtherChild Component</h1>;
};

export default OtherChild;
OtherChild.tsx

挙動の確認

Topページは以下のようになっています。
ReactRouterのLinkコンポーネントを使ってパラメータの設定したリンクを作りました。

アドミンをクリックすると

パラメータが存在するので、Childコンポーネントが表示されます。

メンバーのときも同様です。

ですが、パラメータなしの場合は挙動が変わります。

クエリパラメータが取得できなかったため、OtherChildコンポーネントが表示されました。

このように、パラメータの有無で同じchildパスへのアクセスであっても表示するコンポーネントを分岐させることができました。

まとめ

そもそもv6のオブジェクト形式でルーティングを定義する方法は個人的に見やすくて好きだなと思いました。

また、renderにごちゃごちゃ書くよりも、コンポーネントをしっかり分けたほうが見やすくていです。
ただ、この書き方はrenderにかくとしても使えそうなので覚えておきたいです。

また、同じパスなのに表示させるコンポーネントが違うのはユーザーにとって混乱の原因になったり、逆に処理の複雑化になったりすることもあるので、そもそもルーティングの見直しなども必要なのかなと思いました。

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?