Reactの意義
なぜ私はReactを使いたがるのか。それは間違いなくコンポーネントの恩恵を受けるためでしょう。再利用可能なパーツを組み合わせるUI設計は、我々の開発コストを大きく軽減してくれます。
「同じコードは何度も書きたくない!」
そんなエンジニアの願いを叶えてくれるありがたい存在なんです。
コンポーネント主義
ポートフォリオサイトを作っていた時のことです。私はコンポーネントの恩恵を最大限に享受することを第一に考え、次のような構成を採用しました。画面上部にヘッダーを配置しその下に画面遷移用のタブを置きます。そしてそれより下はメインの要素を表示する部分なので、ルーターによって制御することとしました。
const App = ():JSX.Element => {
return (
<div>
<Header/>
<Tab/>
<BrowserRouter>
<Routes>
<Route path={`/`} element={<Home />} />
<Route path={`/contents`} element={<Contents />} />
<Route path={`/contact`} element={<Contact />} />
</Routes>
</BrowserRouter>
</div>
);
}
export default App;
const Tab = ():JSX.Element => {
return (
<div>
<Link to={'/'}>ホーム</Link>
<Link to={'/contents'}>コンテンツ</Link>
<Link to={'/contact'}>コンタクト</Link>
</div>
);
}
export default Tab;
すごくいい構成だと思いませんか?変更させたい場所だけをルーターの下に配置することで、常に表示させたいヘッダータグやタブタグの登場回数を大幅に削減しているんです。まさに「Reactを使ってよかった〜」と思える瞬間ですね。
さて、みなさんはもうすでにこの設計の欠陥に気づいていることと思いますが、私は気づかないふりをして話を進めます。
デバックの始まり
いいコードが書けましたね。エラーも出ていないようです。早速プレビューを見てみます。
npm start.........
。。。
なんということでしょう。画面が真っ白です!!!
先ほどまで順調に開発を進めていた私の手はピタリと止まりました。エラーは出ていません。でもプレビューが表示されないんです。理由は分かりません。デバックの始まりです。
犯人はRouter
5時間の格闘の末、私は原因を突き止めました。犯人はTabコンポーネントの中のLinkタグです。Linkタグとはreact-router-domから提供されている画面遷移マシンなのですが、どうやらこれを記述する場所に問題があったようです。
もう一度サイトの構成を見てみましょう。
Linkタグが記述されているTabコンポーネントはRouterの外側にあることがわかります。これが原因だったんです。当たり前のことかもしれませんが、LinkタグはRouterの内側に記述しなければならないようです。
修正する
原因が特定できたので、早速修正していきます。といっても簡単です。Tabコンポーネントの位置を変更するだけでOK。ここでもコンポーネントの恩恵を享受することができます。Reactっていいですね。
以下のように各ページにTabコンポーネントを配置すれば良いでしょう。
ジレンマに陥る
なぜ私はLinkタグをRouterの外側に記述してしまったのでしょうか。そう、Tabコンポーネントの登場回数を減らすためです。あれ?おかしいですね。修正版ではページが増えるたびにTabコンポーネントを配置しなければなりません。確かにTab.tsxは一度記述すれば再利用可能なので使い回せばよいのですが、そうは言っても毎回同じ場所に配置するのならRouterの外側に置いておきたいものです。でも仕方ないですよね。Routerの仕様には従うしかありません。
ジレンマから解放される
LinkタブはRouterの内側に記述しなければならないことがわかりました。しかし私はTabコンポーネントの登場回数を減らしたいんです。そして悩み続けた結果案外あっさりと解決してしまいました。
修正版のApp.tsxを見てみます。
BrowserRouter と Routes の間に謎の隙間がありますね。ここに書けばいいじゃん!
今更何を言っているんだという感じですが、私は必死なんです。
const App = ():JSX.Element => {
return (
<div>
<Header/>
<BrowserRouter>
//ここ!!!
<Routes>
<Route path={`/`} element={<Home />} />
<Route path={`/contents`} element={<Contents />} />
<Route path={`/contact`} element={<Contact />} />
</Routes>
</BrowserRouter>
</div>
);
}
export default App;
「というかそもそも BrowserRouter の外側には何も書くな!」という声が聞こえてきそうなので、いっそのことヘッダーコンポーネントもルーターの中に配置してしまいましょう。
つまりこういうこと
const App = ():JSX.Element => {
return (
<div>
<BrowserRouter>
<Header/>
<Tab />
<Routes>
<Route path={`/`} element={<Home />} />
<Route path={`/contents`} element={<Contents />} />
<Route path={`/contact`} element={<Contact />} />
</Routes>
</BrowserRouter>
</div>
);
}
export default App;
まとめ
長い戦いは終わりました。思い返せば本当にしょうもないことなのですが、追い込まれるとこんなことでさえも見失ってしまうんですね。
ルーティング関係は BrowserRouter の内側に記述すること!
そして BrowserRouter の外側の記述はなるべく避けることをお勧めします。