何を作るか?
GitHub や AWS などで、何らかのリソースを削除するときに、次のような確認ダイアログが表示されることがあります。

ユーザーは、削除対象のリソース名をタイピングするまで、削除ボタンを押すことができません。
これは、不用意にリソースを削除してしまわないための措置で、このようなダイアログは様々な場所で活用できます。
ここでは、MUI (Material-UI) で同様のリソース削除確認ダイアログ (AreYouSureDialog
) の実装例を紹介します。
コードをシンプルにするために、表示テキストをハードコーディングしていますが、props に切り出せば汎用的な確認ダイアログとして使えるはずです。
AreYouSureDialog を実装する
完成時のイメージはこんな感じです。 UI のベースは、MUI の Dialog コンポーネントです。

ダイアログコンポーネントの実装
import { FC, useCallback, useState } from 'react'
import {
Button,
Dialog,
DialogContent,
DialogContentText,
DialogTitle,
TextField,
Typography,
IconButton,
} from '@mui/material'
import CloseIcon from '@mui/icons-material/Close'
/** ダイアログコンポーネントの props の型定義 */
type Props = {
/** ダイアログを表示するなら true */
open: boolean
/** 操作対象のリソース名(= ユーザーに入力させるテキスト) */
targetName: string
/**
* ダイアログを閉じるべきときに呼び出されます。
*
* ユーザーが処理を承認したときは、submit の値が true となり、
* 捜査対象のリソース名が targetName に格納されます。
*/
onClose?: (submit: boolean, targetName?: string) => void
}
/**
* ユーザーに確認を促すダイアログコンポーネントです。
* ユーザーは提示されたテキストを入力することで、承認ボタンを押せるようになります。
*/
export const AreYouSureDialog: FC<Props> = ({
open,
targetName,
onClose,
}: Props) => {
// 削除ボタンを有効にするか
const [submitAvailable, setSubmitAvailable] = useState(false)
// ユーザー入力に応じて、削除ボタンの有効・無効を切り替える
const handleInputChange = useCallback(
(input: string) => {
setSubmitAvailable(input === targetName)
},
[targetName, setSubmitAvailable]
)
// ダイアログを閉じるときの処理(クライアント側へコールバックする)
const handleClose = useCallback(
(submit: boolean, targetName?: string) => {
setSubmitAvailable(false)
onClose?.(submit, targetName)
},
[onClose, setSubmitAvailable]
)
return (
<Dialog open={open} onClose={() => handleClose(false)}>
<DialogTitle sx={{ m: 0, p: 2 }}>
本当に削除しますか?
<IconButton
aria-label="close"
onClick={() => handleClose(false)}
sx={{
position: 'absolute',
right: 8,
top: 8,
color: (theme) => theme.palette.grey[500],
}}
>
<CloseIcon />
</IconButton>
</DialogTitle>
<DialogContent>
<DialogContentText>
<Typography gutterBottom>
<b>{targetName}</b>{' '}
を削除すると、リソースに紐づくすべての情報が削除されます。
この処理を取り消すことはできません。
</Typography>
<Typography gutterBottom>
本当に削除するのであれば、<b>{targetName}</b> と入力してください。
</Typography>
</DialogContentText>
<TextField
autoFocus
autoComplete="off"
size="small"
margin="dense"
fullWidth
onChange={(e) => handleInputChange(e.target.value)}
/>
<Button
disabled={!submitAvailable}
variant="contained"
color="error"
sx={{ width: '100%' }}
onClick={() => handleClose(true, targetName)}
>
本当にこのリソースを削除します!
</Button>
</DialogContent>
</Dialog>
)
}
props
の targetName
で渡されるテキストが、ユーザーに入力してもらうテキストになります。
ユーザーが入力エリアでタイピングするたびに handleInputChange
が呼び出され、入力内容が targetName
の値と等しくなったときに削除ボタンを有効化しています。
ダイアログを使う側のサンプルコード
上で実装した AreYouSureDialog
コンポーネントを使う側のサンプルコードです。
Next.js のページ (NextPage
) を想定していますが、React のコンポーネントであれば同様に実装できます。
import { useCallback, useState } from 'react'
import { NextPage } from 'next'
import { Button } from '@mui/material'
import { AreYouSureDialog } from '../components/common/AreYouSureDialog'
/** テスト用のページ */
const SamplePage: NextPage = () => {
const [open, setOpen] = useState(false) // ダイアログの開閉状態
const [result, setResult] = useState('') // 結果確認用
// AreYouSureDialog からの Close イベントをハンドル
const handleClose = useCallback(
(submit: boolean, targetName?: string) => {
// ダイアログを閉じる
setOpen(false)
// 実際にはここでアプリ固有の処理を行う★
setResult(submit ? `submitted with ${targetName ?? ''}` : 'canceled')
},
[setOpen, setResult]
)
return (
<>
<Button onClick={() => setOpen(true)}>Open Dialog</Button>
<div>Result = {result}</div>
<AreYouSureDialog
open={open}
targetName="your-resource"
onClose={handleClose}
/>
</>
)
}
export default SamplePage
ページ上に配置された Open Dialog
ボタンを押すと open
変数が true
にセットされ、ダイアログが表示されます。
ユーザーがダイアログを閉じると handleClose
が呼び出されます。
ここでは、結果を表示しているだけ(コードの★の部分)ですが、実際にはそこでリソースの削除処理などを行います。
関連記事
- MUI のスナックバーを簡単に表示できるようにする (@mui/material/Snackbar)
- MUI でカスタムコンポーネントに sx プロパティを渡せるようにする
- Next.js のページコンポーネントが Client と Server どちらで実行されているか調べる (isServer, isClient, NoSsr)
- Next.js アプリでのリンク方法まとめ(mui/Material-UI との連携なども) (next/link, next/router)
- MUI コンポーネント (v4) に独自のスタイルを設定する (makeStyles)
- Next.js で Material-UI を使う