kakakakakku blog

Weekly Tech Blog : Keep on Learning 👍

Material-UI の Snackbars コンポーネントを実装する

引き続き Material-UI を使ったプロトタイプ開発をしている.今回は Material-UI の Snackbars コンポーネントをサンプルコードを参考に実装しながら理解を深めていく.スナックバー(Snackbars)は Material Design に定義された UI で,画面上にメッセージを一時的に表示する場面で使う.例えば Gmail でメールを削除すると,画面左下に「スレッドをゴミ箱に移動しました。」と表示される.

material-ui.com

なお,実装したサンプルコードは GitHub に公開してある.TypeScript で create-react-app を実行してから実装を進めた.記事に載せるコードはポイントを限定し抜粋するため,実際にコード全体を見る場合は GitHub を参照して頂ければと!

$ create-react-app sandbox-material-ui-snackbars --typescript

github.com

Snackbars コンポーネント

Snackbars コンポーネントで使えるプロパティ一覧は以下のドキュメントに載っている.最初から多くのプロパティを実装すると混乱するため,Step By Step に整理していく.

material-ui.com

anchorOrigin / message / open プロパティ

まず,anchorOrigin プロパティを使うと,スナックバーを表示する位置を指定できる.以下のように horizontal 3種類と vertical 2種類を組み合わせて指定する.

  • horizontal
    • left
    • center
    • right
  • vertical
    • top
    • bottom

message プロパティは,スナックバーにメッセージを表示するために指定する.そして open プロパティに true を指定するとスナックバーが表示されるため,今回は handleClick() を経由して React State open を更新する.React Hooks を前提にするため useState() を使う.プロパティを組み合わせると,以下のような実装になる.

const App: React.FC = () => {
  const [open, setOpen] = React.useState(false);

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

  return (
    <div>
      <button onClick={handleClick}>Open Snackbar 😃</button>
      <Snackbar
        anchorOrigin={{
          vertical: 'bottom',
          horizontal: 'left',
        }}
        open={open}
        message={<span id="message-id">This is Snackbar 🎃</span>}
      />
    </div>
  );
}

実際に動作確認をする.画面左上に配置したボタンをクリックすると,画面左下にスナックバーを表示できる.

f:id:kakku22:20191012222129p:plain

anchorOrigin プロパティを以下のように指定すると,今度は画面右上にスナックバーを表示できる.

anchorOrigin={{
    vertical: 'top',
    horizontal: 'right',
}}

f:id:kakku22:20191012222142p:plain

onClose / autoHideDuration プロパティ

open プロパティと onClose プロパティを組み合わせることにより,表示したスナックバーを非表示にできる.onClose プロパティには関数を指定するため,今回は handleClose() を経由して React State openfalse に更新する.

ただし,onClose プロパティを発火するためには画面をクリックする必要があるため,自動的に非表示にするために autoHideDuration プロパティを組み合わせる.autoHideDuration プロパティは onClose プロパティに指定した関数を実行する前の待機時間となり,単位は milliseconds となる.プロパティを組み合わせると,以下のような実装になる.今回は「5秒」で自動的に非表示になる.

const App: React.FC = () => {
  const [open, setOpen] = React.useState(false);

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

  const handleClose = (event: React.SyntheticEvent | React.MouseEvent, reason?: string) => {
    setOpen(false);
  };

  return (
    <div>
      <button onClick={handleClick}>Open Snackbar 😃</button>
      <Snackbar
        anchorOrigin={{
          vertical: 'bottom',
          horizontal: 'left',
        }}
        open={open}
        autoHideDuration={5000}
        onClose={handleClose}
        message={<span id="message-id">This is Snackbar 🎃</span>}
      />
    </div>
  );
}

onClose プロパティの仕様を読むと,関数の引数に reason があり,「発火理由」を以下の2種類から判断できるようになっている.ドキュメントを読んでも clickaway の意味がわからず,自分なりに調べて挙動を理解できた.

  • timeout : autoHideDuration プロパティにより発火した場合
  • clickaway : スナックバー以外の適当な画面領域をクリックして発火した場合

よって,以下のように clickaway の場合に React State openfalse に更新しないように早期リターンをすると,適当な画面領域をクリックする影響を抑止できる.多くの UI で考慮するべきポイントだと思う.

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

f:id:kakku22:20191012222200p:plain

action プロパティ

action プロパティを指定すると,スナックバーから発火できる追加のアクションを指定できる.以下に抜粋した実装のように Button コンポーネントと IconButton コンポーネントを指定すると,Gmail などでよく見るスナックバーになる.デザインは今回も makeStyles() を定義している(GitHub 参照).

<Snackbar
  anchorOrigin={{
    vertical: 'bottom',
    horizontal: 'left',
  }}
  open={open}
  autoHideDuration={5000}
  onClose={handleClose}
  message={<span id="message-id">This is Snackbar 🎃</span>}
  action={[
    <Button color="secondary" size="medium" onClick={handleClose}>取り消し</Button>,
    <IconButton
      key="close"
      aria-label="close"
      color="inherit"
      className={classes.close}
      onClick={handleClose}
    >
      <CloseIcon />
    </IconButton>,
  ]}
/>

f:id:kakku22:20191012222213p:plain

まとめ

Material-UISnackbars コンポーネントを実装しながら理解を深めた.ドキュメントに載っているサンプルコードは TypeScript にも対応しているし,React Hooks にも対応しているため,合わせて学べてお得感もある.まだまだコンポーネントが多くあるため,引き続き使っていこうと思う.

関連記事

kakakakakku.hatenablog.com