7
5

More than 1 year has passed since last update.

next.js(React)でポップアップメッセージを簡単に呼び出せるようにする

Last updated at Posted at 2022-07-07

はじめに

  • React、next.jsでのWebアプリ開発者向けの内容(本記事の環境はnext.js/TypeScript)
  • 何か操作した時の結果(成功、失敗、警告など)をポップアップメッセージで表示したい、そのための処理を簡単に書きたい人向け
  • Material UIのSnackbarコンポーネントの描画処理を、React Contextでどのコンポーネントからでも簡単に呼び出せるようにする

Snackbar(スナックバー)

  • 短いメッセージをポップアップ表示する
  • Material UIのコンポーネントで提供されており、severity(重要度)のpropsを指定すれば様々なスタイルのSnackbarを表示できる

やりたいこと

  • Webアプリ上で何かイベントが起きた際にsnackbarを表示したい
    • データの追加操作の成功時に成功メッセージを表示
    • 操作に失敗したらエラーメッセージを表示 など
  • snackbarを表示したい全てのコンポーネントで以下のようなコードを書くのは面倒、共通化したい
const [open, setOpen] = React.useState(false);

  const handleClick = () => {
    setOpen(true);
  };

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

    setOpen(false);
  };

  return (
    <Stack spacing={2} sx={{ width: '100%' }}>
      <Button variant="outlined" onClick={handleClick}>
        Open success snackbar
      </Button>
      <Snackbar open={open} autoHideDuration={6000} onClose={handleClose}>
        <Alert onClose={handleClose} severity="success" sx={{ width: '100%' }}>
          This is a success message!
        </Alert>
      </Snackbar>
      <Alert severity="error">This is an error message!</Alert>
    </Stack>
  );

React ContextでSnackbar表示関数をどこからでも呼び出せるようにする

SnackbarProviderコンポーネントを作る

  • 今回は表示する種類(severity)、表示するメッセージ(message)をイベントに応じて変化させたいので、contextに格納する関数 showSnackbarの引数にこの2つを指定。表示位置、表示秒数など他のパラメータを変えたければ引数を増やす。
context-provider.ts
import React, { createContext, useState, useContext, FC } from 'react';
import Snackbar from "@mui/material/Snackbar";
import Alert from '@mui/material/Alert';

type SnackbarSeverity = "error" | "warning" | "info" | "success";

interface ISnackbarContext {
    //snackbarを表示するときに呼び出す関数
    showSnackbar: ((type: SnackbarSeverity, message: string) => void) | undefined;
  }

const SnackbarContext = createContext<ISnackbarContext>({showSnackbar: undefined});

export function useSnackbarContext() {
  return useContext(SnackbarContext);
}

export const SnackbarProvider: FC = ({ children }) => {
    //Snackbarに与えるパラメータをstateで管理
    const [open, setOpen] = useState<boolean>(false);
    const [severity, setSeverity] = useState<SnackbarSeverity>("info");
    const [message, setMessage] = useState<string>("");

    //showSnackbarの実体。各stateをセットし、snackbarを表示する
    const showSnackbar= (type: SnackbarSeverity, message: string): void => {
        setOpen(true);
        setSeverity(type);
        setMessage(message);
    };

    //snackbarのxボタンが押された時のコールバック関数
    const handleClose = (event: React.SyntheticEvent | Event, reason?: string) => {
        if (reason === 'clickaway') {
          return;
        }
    
        setOpen(false);
      };


    return (
        <React.Fragment>
            {/* 子コンポーネントの描画 */}
            <SnackbarContext.Provider value={{showSnackbar: showSnackbar}}>{children}</SnackbarContext.Provider>
            {/* Snackbarの描画 */}
            <Snackbar open={open} autoHideDuration={6000} onClose={handleClose}>
                <Alert onClose={handleClose} severity={severity} sx={{ width: '100%' }}>
                    {message}
                </Alert>
            </Snackbar>
        </React.Fragment>
        
    );
};

SnackbarProviderでラップする

  • 今回はNext.jsで開発している環境のため、_app.tsxでアプリのコンポーネント全体にラップする
pages/_app.tsx
import { SnackbarProvider } from '../components/snackbar-provider';

const App = ({ Component, pageProps }: AppProps) => (
    <div>
        <Head>
            <meta
                name="viewport"
                content="width=device-width, initial-scale=1, shrink-to-fit=no"
            />
        </Head>
            <SnackbarProvider>
                <Component {...pageProps} />
            </SnackbarProvider>
    </div>
);

export default App

showSnackbarを呼び出す

  • 各コンポーネントでuseSnackbarContextをインポートし、Snackbarを表示したいタイミングでshowSnackbarを呼び出す。
  • 以下はDBへのアイテム新規作成するボタンが押された場合に成功、失敗メッセージを表示する例
  • context-provider.tsxのcreateContextを通すためにshowSnackbarの型にundefinedを許容しているため、呼び出し前にif文でshowSnackbarの存在確認が必要
create-item.tsx
import { useSnackbarContext } from "../../components/snackbar-provider";

export const CreatePage: React.FC = () => {
    const { showSnackbar } = useSnackbarContext();
    const onCreateClick = (item: Item) => {
            addItem(item)
                .then(() => {
                    if(showSnackbar){
                        //成功のSnackbar表示
                        showSnackbar("success", "アイテムが新規作成されました")
                    }
                })
                .catch((error) => {
                    if(showSnackbar){
                        //エラーメッセージのSnackbar表示
                        showSnackbar("error", error.message)
                    }
        }
    };

    return (
        <div>
            <InputItem onSubmit={onCreateClick} />
        </div>
    );
};

まとめ

  • React ContextでSnackbar表示関数をどのコンポーネントからも簡単に呼び出せるようにした
  • 表示位置、サイズ、自動非表示の有無などもshowSnackbarの引数で制御できるようにすれば、重要メッセージは画面中央、大きく、消えずに表示させるなど、汎用性を高めることもできそう
7
5
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
7
5