【Next.js, MUI】フラッシュメッセージを表示させるベストプラクティス

2023年11月24日 17:09

【Next.js, MUI】フラッシュメッセージを表示させるベストプラクティス

フラッシュメッセージはユーザーフレンドリーのために欠かせないパーツ。ユーザーが操作するたびに表示させるものなので、一つ一つ個別に作っていくのではなく、プロジェクトの中でグローバルに呼び出せる方法を考えてみる。

状態管理ライブラリを駆使しながら実装していく

目次
ライブラリ

ライブラリ

  • react@18.2.0
  • next@14.0.3
  • @mui/material@5.14.12
  • jotai@2.4.3

フラッシュメッセージとは

例えば、こんなやつ。Webアプリケーションで多数登場することになる。

グローバル関数callFlashMessageを作成し、プロジェクトのどこからでも呼び出せるようにする。

まずは状態管理

フラッシュメッセージを制御するための情報を状態管理に持たせる。今回は、jotaiライブラリを使う。

import { AlertColor, SnackbarProps } from "@mui/material";
import { atom } from "jotai";

export const snackbarAtom = atom<{severity?: AlertColor, loading: boolean} & SnackbarProps>({
  loading: false,
  autoHideDuration: 5000,
})

MUIのsnackbarのpropsに加えて、loadingとserverityプロパティを設定。

loading→trueの場合、サーキュレートインディケータをつけて永久表示させる。デフォルトでは5秒間表示。

serverity→AlertColorつまり'info' | 'success' | 'warning' | 'error'の中から色を選べるように。

コンポーネント作成

FlashMessageコンポーネントを作る。グローバルフラッシュメッセージなるものを作りたいので、コンポーネントはlayout.tsx付近に配置

'use client'

import { snackbarAtom } from "@/atom/snackbar";
import { Alert, CircularProgress, Slide, Snackbar } from "@mui/material";
import { useAtom } from "jotai";
import { FC, SyntheticEvent, useEffect, useState } from "react";
import { IconButton } from "@mui/material";
import CloseIcon from '@mui/icons-material/Close';

const close = (handleClose) => (
  <IconButton
    size="small"
    onClick={handleClose}
    color="inherit"
  >
    <CloseIcon fontSize="small" />
  </IconButton>
)

export const FlashMessage: FC = () => {
  const [ { key, loading, children, message, severity, ...props}, setProps ] = useAtom(snackbarAtom)
  const defaultDuration = 5000
  const [ autoHideDuration, setAutoHideDuration ] = useState(defaultDuration)

  useEffect(() => {
    setAutoHideDuration(loading ? null : defaultDuration)
  }, [ loading ])

  const handleClose = (event: SyntheticEvent | Event, reason?: string) => {
    if (reason === 'clickaway') {
      return;
    }

    setProps({ key, loading, children, message, severity, ...props, open: false });
  }

  const action = close(handleClose)

  return(
    <Snackbar
      key={key || 'snackbar'}
      action={action}
      onClose={handleClose}
      autoHideDuration={autoHideDuration}
      message={message}
      TransitionComponent={(props) => <Slide { ...props } direction="right" />}
      {...props}
    >{children || severity && (
      <Alert
        onClose={handleClose}
        severity={severity}
        sx={{ width: '100%' }}
        icon={loading ? <CircularProgress color="inherit" size={22} /> : undefined}
      >
        {message}
      </Alert>
    )}</Snackbar>
  )
}

callFlashMessageの作成

ここまでで、useAtomを使いながらグローバルにフラッシュメッセージを呼び出そうと思えば呼び出せる。

import { snackbarAtom } from '@/atom/snackbar.ts'

snaconst [ snackbar, setSnackbar] = useAtom(snackbarAtom)

setSnackbar({
  ...snackbar,
  key: 'flashMessage',
  loading: true,
  severity: 'info',
  message: '〇〇処理を開始しました。',
  open: true
})

…が、いちいちsnackbarのpropsを意識しなといけないので、簡単に呼び出せるよう自作関数callFlashMessageを作る。

こちらの記事を参考に、まずstoreを設置。

export const store = createStore()

<JotaiProvider store={store}>{children}</JotaiProvider>

storeを使って制御するための関数を作成。

import { store } from '@/components/Atom/Provider'

type Action = 'start' | 'success' | 'fail'
export const callFlashMessage = (action: Action, message: string) => {
  let severity;
  switch(action) {
    case 'start':
      severity = 'info';
      break;
    case 'success':
      severity = 'success'
      break
    case 'fail':
      severity = 'error'
      break
    default:
      severity = undefined
      break
  }

  store.set(snackbarAtom, ((prevState) => ({
    ...prevState,
    key: 'flashMessage',
    open: true,
    loading: action == 'start',
    message,
    severity
  })))
}

引数には、actionとmessageを設定。actionには'start' | 'success' | 'fail'の3種類を許可。正直、フラッシュメッセージの種類としてはたいていこの3つだけでいい。

callFlashMessage('start', '〇〇処理を開始しました。')
callFlashMessage('success', '〇〇処理が完了しました。')
callFlashMessage('fail', '〇〇処理が失敗しました。')

もっとカスタマイズしたければ、自作関数も自由に増やせばいい。

まとめ

何回も呼び出すフラッシュメッセージは、より簡単に!!

プログラミング記事の一覧に戻る