🌟
[ReactRouter] SaaSなどでありがちな、複数企業への所属と切り替えをURLパラメータで表現する方法
概要
SasS や toB のウェブアプリでは、1つのアカウントが複数のグループに所属することが起こります。ここでのグループとは、企業や団体やチームなどを指します。
そして、現在どのグループでログインしているかを、URL 内の ID やハッシュ値で表現することがあるかと思います。
例: /app/12345/users
(12345がID部分)
それを React Router でどのように実装するかの備忘録です。
※記事中のコードはあくまでサンプルです。実際に動かすにはいくつかの修正が必要かもしれません。
環境
この記事を執筆している時点で、各ライブラリのバージョンは以下のとおりです。
- react: ^18.0.0
- react-router-dom: ^6.1.0
実装
まず、アプリケーションのルーティング定義を以下のように行います。
import { createBrowserRouter } from 'react-router-dom';
import { AuthCheck } from 'path/to/component'
const router = createBrowserRouter([
// 認証不要
{ path: 'login', element: // ...
// 要認証
{
path: '',
element: <AuthCheck />, // 親コンポーネント
children: [
// ここでいったん全てのリクエストを受け付ける
{ path: 'company/*', element: null },
{ path: 'company/:company_hash/users', element: null },
{ path: 'company/:company_hash/users/:user_id', element: null },
// これは存在しないURLへの対策
{ path: 'company/:company_hash/*', element: null },
// ... その他のパスたち
]
}
])
とあるアカウントが企業A(ハッシュ値: xxxxx)と企業B(ハッシュ値: yyyyy)に所属しているとします。企業Aのユーザーを見る時は /company/xxxxx/users
に、企業Bのユーザーを見るときは /company/yyyyy/users
にアクセスすることになります。
次に、 AuthCheck コンポーネントです。
import { useState, useEffect } from 'react'
import { useLocation, useNavigate, useParams, generatePath } from 'react-router-dom';
export const AuthCheck = ({ children }) => {
const navigate = useNavigate();
const location = useLocation();
const params = useParams();
const [ok, setOk] = useState(false)
// 認証チェックとハッシュ値の自動付与を行う
const checkAuth = async () => {
// 認証チェックAPIを叩く。レスポンスにはアカウント情報を含める。
const user = await fetch('...
// ここで未認証の場合の処理
// 現在ログイン中の企業ハッシュ値
const currentHash = user.company.hash
// 所属企業のハッシュ値一覧
const companyHashes = user.companies.map(company => company.hash)
// URLパラメータには何でも入るので、この確認が必要
const isValidParam = companyHashes.includes(params.company_hash)
// ハッシュ値が変わっている
if (isValidParam && params.company_hash !== currentHash) {
// ここで必要に応じて、指定企業への再ログイン処理など
return;
}
// URLパラメータの自動付与
if (!isValidParam) {
// ここの URL 組み立てはお好みの方法でOK
const newPathname = generatePath('/company/:company_hash/*', {
company_hash: currentHash,
'*': location.pathname.split('/company/')[1],
});
const newPath = `${newPathname}${location.search}`;
return navigate(newPath, { replace: true });
}
setOk(true)
}
useEffect(() => {
checkAuth()
}, [location]) // ページ遷移ごとにチェックしたい
return !ok ? null : children
}
ハッシュ値の付与を AuthCheck で統一的に行うことで、アプリケーションの各所ではハッシュ値なしでページ遷移を指定できます。
navigate("/company/users") // /company/:company_hash/users に遷移
<Link to="/company/users/1" /> // /company/:company_hash/users/1 に遷移
また、企業の切り替えもページ遷移すればOK。
<Link to="/company/yyyyy/users">
企業Bのユーザー一覧
</Link>
以上。
Next.js や Remix を使うとまた違った実装になりそうですが、どこかで統一的に処理した方が良いという点では、共通するものはあるかと思います。
Discussion