[React + Typescript + MUI] アイコン付きテキストナビゲーションの作り方
概要
本記事は、以前に作成した[React + Typescript + MUI]テキストナビゲーションの作り方の続きです。MUIのアイコンを使ってアイコン+テキストのナビゲーションを作成します。
また、リストのアイテム数が増えるとコードが冗長になるのでリファクタリングを行いコード量を減らします。
前提
- React + Typescriptの環境が構築済みである
- react-router-domおよび@mui/materialがインストール済みである
- Link, NavLink, List, ListItem, ListItemButton, ListItemTextについて理解がある
- ソースコードは任意のレイアウト用ファイル
LayoutTest.tsx
に記述しています。
以前の投稿でまとめていますのでそちらもご覧ください
パッケージのインストール
@mui/icons-material
npm install @mui/icons-material
MUIのアイコンを取得する
MUIにはアイコンが用意されており、インポートをすることで簡単に利用できます。
今回は、HomeとAboutに合うアイコンを取得してインポートします。
- Googleで"mui icon"で検索するか、Material Iconsにアクセスする
- Search Material Iconsの検索用テキストボックスに"Home"と入力して、使いたいアイコンをクリックする(ここではHomeを選択)
- ダイアログボックスが表示されるので、imort文をコピーしてソースコードのに貼り付ける
- Aboutも同様に適当な文字列で検索して使用するアイコンのimport文をソースコードに貼り付ける(ここでは"AccountCircle"を選択)
MUIアイコンをアプリで使う
取得したimport文をソースコードに貼り付けて、使いたい場所でコンポーネントを呼び出します。
アイコンをインポートする
import HomeIcon from '@mui/icons-material/Home'
リストにアイコンをセットする
アイコンを表示するにはインポートしたコンポーネント名をListItemIcon
で囲みます
<ListItemIcon>
<[アイコン名] />
</ListItemIcon>
アイコン付きテキストナビゲーションのソースコード
import React from 'react'
import { List, ListItem, ListItemButton, ListItemText } from '@mui/material'
import { NavLink } from 'react-router-dom'
import HomeIcon from '@mui/icons-material/Home'
import AccountCircleIcon from '@mui/icons-material/AccountCircle'
const LayoutTest = () => {
return (
<div>
<List>
<NavLink to="/">
<ListItem disablePadding>
<ListItemButton>
<ListItemIcon>
<HomeIcon />
</ListItemIcon>
<ListItemText primary="Home" />
</ListItemButton>
</ListItem>
</NavLink>
<NavLink to="/about">
<ListItem disablePadding>
<ListItemButton>
<ListItemIcon>
<AccountCircleIcon />
</ListItemIcon>
<ListItemText primary="About" />
</ListItemButton>
</ListItem>
</NavLink>
</List>
</div>
)
}
export default LayoutTest
このソースコードの問題点
とりあえず動くものはできましたが、このままではアイテムごとに同じ構成のNavLink
が繰り返されているため読みにくいしコードが冗長なのでリファクタリングをします。
リファクタリング
リファクタリングの方針
- アイテムの内容(リンク先, テキスト, アイコンのコンポーネント名)を配列にセットする
- map関数を使って配列を個々に取り出してアイテムにセットする
アイテム配列の作成
アイテムの配列は個別のアイテムをセットしたオブジェクトをセットします
オブジェクトのプロパティと型を定義(interface)
Typescriptなのでオブジェクトのプロパティと型を設定します。
このインターフェースはリンク先のURL, 表示テキスト, アイコンのコンポーネントを設定します。
なお、アイコンの型がReact.ComponentType
なのは、MUIのアイコンはReactのコンポーネントとしてエクスポートされるからです。
少しややこしいですがMUIアイコンを使うときのお作法と考えればいいと思います。
interface NavigationItem {
to: string // リンク先
text: string // 画面に表示するテキスト
icon: React.ComponentType // インポートしたMUIアイコン名
}
データをアイテム配列に格納する
実際に設定するデータを配列にセットします。NavigationItem
配列に各アイテムのオブジェクトをセットしています。
上で宣言したNavigationItem
インターフェースを配列の型にしています。配列ですのでNavigationItem[]
となります。
const navigationItems: NavigationItem[] = [
{ to: '/', text: 'Home', icon: HomeIcon },
{ to: '/about', text: 'About', icon: AccountCircleIcon },
]
map関数を使って配列を個々に取り出してアイテムにセットする
アイテムオブジェクトを配列に入れた理由はmap関数を使って配列の要素の数だけNavLink
を生成するためです。
※読みやさ重視のためNavLink
のclassName
オプションは消しています
<List>
{navigationItems.map((item, index) => (
<NavLink to={item.to} key={index}>
<ListItem disablePadding>
<ListItemButton>
{<item.icon />}
<ListItemText primary={item.text} />
</ListItemButton>
</ListItem>
</NavLink>
))}
</List>
map関数実装時のポイント
- map関数の構文は
{items.map((item, index) =>(処理))}
となり、全体を{}
で囲む必要があります。 -
NavLink
のkey
オプションはmap関数を使うときはループごとにユニークなkey
設定しなければならないという仕様のためにindex
をキーにしています。
完成コード
import React from 'react'
import { List, ListItem, ListItemButton, ListItemText } from '@mui/material'
import { NavLink } from 'react-router-dom'
import HomeIcon from '@mui/icons-material/Home'
import AccountCircleIcon from '@mui/icons-material/AccountCircle'
interface NavigationItem {
to: string
text: string
icon: React.ComponentType
}
const navigationItems: NavigationItem[] = [
{ to: '/', text: 'Home', icon: HomeIcon },
{ to: '/about', text: 'About', icon: AccountCircleIcon },
]
const LayoutTest = () => {
return (
<div>
<List>
{navigationItems.map((item, index) => (
<NavLink
to={item.to}
key={index}
className={({ isActive }) => (isActive ? 'active-link' : 'inactive-link')}
>
<ListItem disablePadding>
<ListItemButton>
{<item.icon />}
<ListItemText primary={item.text} />
</ListItemButton>
</ListItem>
</NavLink>
))}
</List>
</div>
)
}
export default LayoutTest
留意点や感想など
- ナビゲーションはHTMLだとすぐに書けますが, Reactにすると難しくかなり勉強することになりました。特にTypescriptは難しいですね。。
- 次は今回作ったナビゲーションをコンポーネント化し、アイテム配列を
pops
で渡すなど再利用を意識したものに変えようと思います
Discussion