Очередь уведомлений

Библиотека MUI (Material-UI) предоставляет мощные инструменты для работы с компонентами интерфейса в React, включая систему уведомлений. Основной компонент для отображения временных уведомлений — это Snackbar. Для реализации очереди уведомлений, когда несколько уведомлений появляются одно за другим, MUI предлагает гибкие подходы через состояние и контекст приложения.


Компонент Snackbar

Snackbar используется для отображения кратких сообщений пользователю. Основные свойства:

  • open — булево значение, определяющее видимость уведомления.
  • message — текст уведомления.
  • autoHideDuration — время в миллисекундах, через которое уведомление автоматически скрывается.
  • onClose — функция, вызываемая при закрытии уведомления.
  • action — дополнительные элементы интерфейса (например, кнопка «Закрыть»).

Пример базового использования:

import React, { useState } from 'react';
import Snackbar from '@mui/material/Snackbar';
import Button from '@mui/material/Button';

function SimpleSnackbar() {
  const [open, setOpen] = useState(false);

  const handleClick = () => setOpen(true);
  const handleClose = (event, reason) => {
    if (reason === 'clickaway') return;
    setOpen(false);
  };

  return (
    <>
      <Button onCl ick={handleClick}>Показать уведомление</Button>
      <Snackbar
        open={open}
        autoHideDuration={3000}
        onCl ose={handleClose}
        message="Пример уведомления"
      />
    </>
  );
}

Очередь уведомлений

Для корректного управления несколькими уведомлениями требуется очередь. Стандартный Snackbar отображает только одно уведомление за раз, поэтому нужно хранить массив уведомлений и последовательно их показывать.

Структура очереди
  • queue — массив объектов уведомлений: { message: string, key: number, variant?: 'success' | 'error' | 'info' }.
  • current — текущее уведомление, отображаемое в Snackbar.
  • open — состояние видимости текущего уведомления.
Реализация очереди
import React, { useState, useEffect } from 'react';
import Snackbar from '@mui/material/Snackbar';
import Button from '@mui/material/Button';

let keyCounter = 0;

function SnackbarQueue() {
  const [queue, setQueue] = useState([]);
  const [current, setCurrent] = useState(null);
  const [open, setOpen] = useState(false);

  const enqueueSnackbar = (message) => {
    const key = keyCounter++;
    setQueue((prev) => [...prev, { message, key }]);
  };

  useEffect(() => {
    if (!current && queue.length > 0) {
      setCurrent(queue[0]);
      setQueue((prev) => prev.slice(1));
      setOpen(true);
    }
  }, [queue, current]);

  const handleClose = (event, reason) => {
    if (reason === 'clickaway') return;
    setOpen(false);
  };

  const handleExited = () => {
    setCurrent(null);
  };

  return (
    <>
      <Button onCl ick={() => enqueueSnackbar('Первое уведомление')}>Добавить уведомление</Button>
      <Snackbar
        key={current?.key}
        open={open}
        message={current?.message}
        autoHideDuration={3000}
        onCl ose={handleClose}
        onExi ted={handleExited}
      />
    </>
  );
}

Управление стилями и вариантами

Для отображения уведомлений с разными стилями (success, error, info, warning) удобно использовать Alert внутри Snackbar:

import Alert from '@mui/material/Alert';

<Snackbar
  key={current?.key}
  open={open}
  autoHideDuration={3000}
  onCl ose={handleClose}
  onExi ted={handleExited}
>
  <Alert onCl ose={handleClose} severity={current?.variant || 'info'}>
    {current?.message}
  </Alert>
</Snackbar>

Свойство severity задаёт цвет иконки и фон уведомления, что повышает наглядность и удобство восприятия пользователем.


Контекст уведомлений для всего приложения

Чтобы использовать очередь уведомлений глобально, создают SnackbarContext:

import React, { createContext, useContext, useState } from 'react';

const SnackbarContext = createContext();

export const useSnackbar = () => useContext(SnackbarContext);

export function SnackbarProvider({ children }) {
  const [queue, setQueue] = useState([]);
  const [current, setCurrent] = useState(null);
  const [open, setOpen] = useState(false);

  const enqueueSnackbar = (message, variant = 'info') => {
    const key = new Date().getTime() + Math.random();
    setQueue((prev) => [...prev, { message, key, variant }]);
  };

  React.useEffect(() => {
    if (!current && queue.length > 0) {
      setCurrent(queue[0]);
      setQueue((prev) => prev.slice(1));
      setOpen(true);
    }
  }, [queue, current]);

  const handleClose = (event, reason) => {
    if (reason === 'clickaway') return;
    setOpen(false);
  };

  const handleExited = () => setCurrent(null);

  return (
    <SnackbarContext.Provider value={{ enqueueSnackbar }}>
      {children}
      {current && (
        <Snackbar
          key={current.key}
          open={open}
          autoHideDuration={3000}
          onCl ose={handleClose}
          onExi ted={handleExited}
        >
          <Alert onCl ose={handleClose} severity={current.variant}>
            {current.message}
          </Alert>
        </Snackbar>
      )}
    </SnackbarContext.Provider>
  );
}

Теперь любой компонент в приложении может использовать useSnackbar() для добавления уведомлений, не заботясь о реализации очереди и отображении.


Дополнительные возможности

  • Позиционирование уведомлений: через свойство anchorOrigin можно выбрать место на экране: { vertical: 'top', horizontal: 'right' }.
  • Пользовательские действия: через action можно добавить кнопки «Отмена», «Повторить», открытие модальных окон.
  • Анимации: MUI автоматически применяет плавные переходы, но можно кастомизировать через свойства TransitionComponent.

Эта система позволяет создавать масштабируемую и управляемую очередь уведомлений с разнообразными стилями, действиями и гибкой интеграцией в приложение на React с MUI. Она легко расширяется для сложных интерфейсов, где важно последовательное и ненавязчивое информирование пользователя.