【MUI v5】メモしておきたいMenuの使い方
開発環境
詳しい開発環境はこちら
はじめに
新人エンジニアのこはるです。
仕事では主にJavaを使っているSES社員です。
個人開発をするときに調べたMUIコンポーネントの使い方をメモする為に書きました。
なので、使い方の全ては網羅していません。ご了承ください。
Menuコンポーネント編です。
↓公式ドキュメント
勉強しながらコードを書いていっている途中なので、もし間違いとかがありましたら優しく指摘してください。
他のコンポーネントについてはこちら
使い方早見
簡単なコードの使い方早見です。
今回はMenuコンポーネントのメモなので、他コンポーネントの記述は最小限です。
const Menu: FC = () => {
// メニューの開閉を管理
const [open, setOpen] = useState<boolean>(false);
// メニューを配置するHTML要素を格納する
const anchorEl = useRef<HTMLButtonElement>(null);
// メニュー開閉ハンドル
const handleClick = () => {
setOpen(!open);
};
// メニューを閉めるハンドル
const handleClose = () => {
setOpen(false);
};
return (
<>
<Button
// ボタンのHTML要素
ref={anchorEl}
// ボタンのタイプ
variant="contained"
// クリックアクション
onClick={handleClick}
// Style
sx={{ m: 5 }}
>
ボタン
</Button>
<Menu
// ここでボタンの位置にメニューを紐づける
// この紐づけのお陰でメニューがボタンの隣に出現する
// これが無いと画面の変なところでメニューが出現することになる
anchorEl={anchorEl.current}
// メニューの出現を管理
open={open}
// Falseだと、メニューを開いた時にメニューアイテムがフォーカスの対象になる
disableAutoFocusItem={false}
// Trueだとメニューが開いた時に一番上のメニューアイテムのオートフォーカスされる
autoFocus={false}
// 主にメニューを閉めたいときに発生するイベント
onClose={handleClose}
// Trueにすると、メニューが閉じている状態でもメニューのノードが存在するようになる
keepMounted
// メニューの開閉のアニメーション速度を設定できる
transitionDuration={"auto"}
// CSS in JS を記述できる(HTMLのstyle属性の役割を果たす)
sx={{}}
// 紐づけたHTML要素のどこを標準位置にしてメニューを配置するか設定できる
anchorOrigin={{
vertical: "bottom",
horizontal: "right"
}}
// メニューの起点を設定できる。アニメーションもこの起点から生えるように出現する
transformOrigin={{
vertical: "top",
horizontal: "right"
}}
// Menuコンポーネント内部で使用されているMenuListコンポーネントのPropsを変更できる
MenuListProps={{}}
// Menuコンポーネント内部で使用されているPaperコンポーネントのPropsを変更できる
PaperProps={{
// PaperProps.elevationはメニューのシャドーを調整できる(超重要!)
elevation: 3
}}
>
<MenuItem onClick={handleClose}>メニュー1</MenuItem>
<MenuItem onClick={handleClose}>メニュー2</MenuItem>
<MenuItem onClick={handleClose}>メニュー3</MenuItem>
</Menu>
</>
);
};
CodeSandboxのサンプル
CodeSandboxにサンプルを作成しました。
挙動はこちらで確認してください。
解説
公式の使用例とは少し違う形です。
openとsetOpenで開閉を制御しています。
こっちの方が自分としては分かりやすく、融通が利くと思っています。
ただこれは好みもあると思いますので、自身のやりやすいコードか、現場に合ったコードで書いていけばいいでしょう。
また、ひとつMenuの注意点として、
見落としやすいですがMenuはMenu APIで記載されているPropsの他にPopover APIとModal APIのPropsも使うことができます。
↓ここからPropsの解説に入ります。
anchorEl
ここに紐づけ用のHTML要素を指定すると、メニューがそのHTML要素を目印にポップするようになります。
これが無いと、メニューは画面の変なところからポップします。
早見では、MUIのButtonコンポーネントのref属性からuseRefでHTML要素を取得して、それをanchorElに指定しています。
これにより、メニューはButtonの隣にポップするようになります。
open
メニューの出現を管理します。
Trueで開く。
Falseで閉じる。
分かりやすいですね。
早見では、useStateのopne変数で制御しています。
disableAutoFocusItem
Falseだと、メニューを開いた時にメニューアイテムがフォーカスの対象になります。
デフォルトはFalseです。
CodeSandboxのサンプルブラウザーをクリックした後に、Tabでボタンをフォーカス
↓
Enterでメニューを開いてください

メニュー1が灰色にオートフォーカスされましたでしょうか?
Trueに設定すると、このオートフォーカスが起こらなくなります。
メニューが開いた状態でもう一度Tabを押下するとメニュー1にフォーカスされます。あくまでMenuの子要素(この場合はMenuItem)に対するオートフォーカスが無くなるだけで、Menuにはフォーカスされます
特別な理由が無い限り、ここはデフォルトのFalseで良いでしょう。
autoFocus
Trueだとメニューが開いた時に一番上のメニューアイテムにオートフォーカスされます。
デフォルトはTrueです。
これは前述のdisableAutoFocusItemに似ていて非常に分かりにくいのですが、
disableAutoFocusItemはTrueに設定すると
「Menuの子要素にはフォーカスされないが、Menu自体にはフォーカスが残る」
のに対し、
autoFocusをFalseに設定すると
「Menu自体にもフォーカスされない」
という挙動が起こります。
autoFocusをFalseにすると「アクセシビリティに重大な影響を与える」とMUIの公式ドキュメントにも記載がありますので、こちらも特別な理由が無い限り、デフォルトのTrueにしてください。
onClose
主にメニューを閉めたいときに発生するイベントハンドラーです。
メニューのポップ中に
- Escapeキーの押下
- Tabキーの押下
- メニュー以外の場所をクリック
のいづれかが発生したときにonCloseのイベントが発火します。
メニューを閉じる関数を指定するのが一般的でしょう。
keepMounted
Trueにすると、メニューが閉じている状態でもMenuのノードが存在するようになります。
デフォルトはFalseです。
Google ChromeのDevToolsを使ったら分かるのですが、Menuは閉じているときDOMにノードが存在しません。
keepMountedをTrueにすることで、Menuが閉じている状態でも「見えないだけでノードは存在している」というふうにできます。
これはSEOの対策や、メニューのポップ速度を最大化したいときに使用できます。
transitionDuration
メニューの開閉のアニメーション速度を設定できます。
デフォルトはautoです。
速度の単位はms(ミリ秒)で、Number型で設定できます。
他にも、Object型{ appear?: number, enter?: number, exit?: number }でそれぞれのメンバーを個別に設定することで、出現時、エンター時、終了時それぞれの速度を細かく設定できます。
sx
CSS in JS を記述できます。
役割はHTMLのstyle属性とほぼ同じです。
但し、型はObject型です。
詳しくは公式ドキュメントを参照
anchorOrigin
紐づけたHTML要素のどこを標準位置にしてメニューを配置するか設定できます。
データ型は、
{
horizontal: 'center' | 'left' | 'right' | number,
vertical: 'bottom' | 'center' | 'top' | number
}
です。
早見では右下{vertical: "bottom",horizontal: "right"}に設定しているため、メニューはボタンの右下からポップします。
transformOrigin
メニューの起点を設定できます。
アニメーションもこの起点から生えるように出現します。
データ型は、
{
horizontal: 'center' | 'left' | 'right' | number,
vertical: 'bottom' | 'center' | 'top' | number
}
です。
早見では右上{vertical: "top",horizontal: "right"}に設定しているため、メニューは右上を起点にポップします。
MenuListProps
Menu内部で使用されているMenuListのPropsを変更できます。
MenuListのPropsについては公式ドキュメントを参考にしてください。
PaperProps
Menu内部で使用されているPaperの属性を変更できます。
PaperProps.elevationを利用すると、メニューのシャドーを調整できます!
ここ重要です!
PaperProps={{
elevation: 3
}}
PaperのPropsについては公式ドキュメントを参考にしてください。
カスタマイズしたメニュー
こちらでは、カスタマイズしたメニューを載せていきます。
普通のMenuとは違う機能を使いたいときに利用します。
身も蓋もありませんが、
ぶっちゃけ、私はMenuよりもこっちを基本的に使う事になると思っています。
何故かというと、デフォルトのMenuはユーザビリティで欠点を抱えていると私は考えているからです。
CodeSandboxのサンプルを確認していただきたいのですが、左側のボタンのメニューを開いた状態で、右側のボタンを押そうとすると直ぐに押せないですよね。
一度クリックでメニューを閉じないと、他の操作ができません。

これはマウス操作に慣れている人だと、恐らく2、3回はクリックを空振りすると思います。
(スマホでも、タップしても中々操作ができず数回タップして「おっ、やっと開いた」となるはずです)
こうした戸惑いやちょっとしたイラだちは、ユーザー体験(UX)的にあまりよろしくないでしょう。
これが、デフォルトのMenuの欠点です。
(これ、実はMenuだけでどうにかなるんでしょうか? できるなら恥ずかしいのですが……(/ω\))
カスタマイズのCodeSandboxのサンプル
この欠点を無くしたカスタマイズがこちらです。
メニューがポップしている状態でも、別の場所をクリックできるようになっています。
Menuは元々、PopperやGrow、PaperなどのMUIの別コンポーネントの組み合わせなので、それを自分でつくった感じになります。
コンポーネントの分割などちゃんと出来ていないので、このままだと使いずらいですが……
いずれもっと使いやすいように改良します。
まとめ
今回はMenuの使い方をメモしました。
他のコンポーネントについてはまた次回。
結構まとめるのに時間が掛かったので、ちょっとずつ追加していこうと思います。
それではまた(´・ω・`)ノシ
Discussion