Компонент React Material-UI Snackbar — хранение нескольких закусочных в состоянии

Я использую библиотеку React MUI с NextJS.

Я пытаюсь создать функцию, в которой сгенерированные закусочные хранятся в массиве в магазине (используя избыточную библиотеку под названием Easy-Peasy), а затем отображаются один за другим по истечении времени ожидания.

Например. если есть 3 закусочные из-за трех ошибок пользователя - то:

  1. Сначала появится закусочная, а затем тайм-аут через 4 секунды.

  2. Появится секунда, а затем 4-секундный тайм-аут

  3. Третий будет отображаться, а затем тайм-аут через 4 секунды

Если сгенерирована одна закусочная, она будет работать нормально, т.е. исчезнет по истечении времени ожидания.
Если есть две или более закусочных, то исчезнет только одна, оставив позади n-1 закусочные, например. если в массиве 3, один исчезнет, ​​оставив два в очереди. Второй никогда не исчезнет по тайм-ауту.

import { useState, useEffect } from 'react'
import { Snackbar, Alert } from '@mui/material'

export default function SnackBar(props) {
  const snackbarData = []
  // this will be an array from the store which is structured: 
  // [
  //   { id: 1, message: 'message1', severity: 'error' },
  //   { id: 2, message: 'message2', severity: 'error' }
  // ]
  const removeSnackbarMessage = () => { 
    // a function which performs a .shift() method on the snackbarData array within the store 
  }

  const [open, setOpen] = useState(false)
  const [message, setMessage] = useState('')
  const [severity, setSeverity] = useState('')

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

  useEffect(() => {
    if (snackbarData.length > 0) {
      setOpen(true)
      setMessage(snackbarData[snackbarData.length - 1].message)
      setSeverity(snackbarData[snackbarData.length - 1].severity)
    } else {
      setOpen(false)
      setMessage('')
      setSeverity('')
    }
  }, [snackbarData, snackbarData.length])
    
  return (
    <Snackbar 
      open = {open} 
      autoHideDuration = {4000} 
      anchorOrigin = {{ horizontal: 'right', vertical: 'bottom'}}
      onClose = {handleClose}
    >
      <Alert severity = {severity}>{message}</Alert>
    </Snackbar>
  )
}

Выше приведен код, содержащийся в компоненте <SnackBar />, на который ссылается index.js.

Любая помощь приветствуется. ТИА.

Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Навигация по приложениям React: Исчерпывающее руководство по React Router
Навигация по приложениям React: Исчерпывающее руководство по React Router
React Router стала незаменимой библиотекой для создания одностраничных приложений с навигацией в React. В этой статье блога мы подробно рассмотрим...
Массив зависимостей в React
Массив зависимостей в React
Все о массиве Dependency и его связи с useEffect.
0
0
57
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Проблема в том, что сообщения, которые вы хотите «поставить в очередь», просто перезаписывают одно сообщение, хранящееся в состоянии. Вам нужно создать очередь (массив) сообщений, но в этом случае нельзя использовать свойство по умолчанию autoHideDuration, поскольку оно предназначено только для одного сообщения.

Вот рабочий пример системы уведомлений Snackbar с очередями, использующей React18 и MUI, которую вы можете привязать к магазину.

index.js

import { StrictMode } from "react";
import { createRoot } from "react-dom/client";

import App from "./App";

const rootElement = document.getElementById("root");
const root = createRoot(rootElement);

root.render(
  <StrictMode>
    <App />
  </StrictMode>
);

App.js

import "./styles.css";
import { useState } from "react";
import Button from "@mui/material/Button";
import Snackbar from "./Snackbar";

export default function App() {
  // create some initial error messages, which already worked fine
  const [snackbarData, setSnackbarData] = useState([
    { id: 1, message: "First default message", severity: "error" },
    { id: 2, message: "Second default message", severity: "error" },
    { id: 3, message: "Third default message", severity: "error" },
  ]);

  // create a random error for the snackbar queue, this normally caused the issue
  const createError = () => {
    setSnackbarData((d) => [
      ...d,
      {
        id: 3,
        message: `Error Message ${Math.random()}`,
        severity: "error",
      },
    ]);
  };

  return (
    <div className = "App">
      <Button variant = "contained" color = "error" onClick = {createError}>
        Create Error
      </Button>
      <Snackbar snackbarData = {snackbarData} setSnackbarData = {setSnackbarData} />
      {snackbarData.length ? (
        <div style = {{ marginTop: "1rem" }}>
          <b>Queued messages</b>
          {snackbarData
            .filter((_i, key) => key > 0)
            .map((msg, key) => (
              <div
                key = {`msg-${key}`}
                style = {{ borderTop: "1px solid black", margintop: ".25rem" }}
              >
                Message: {msg.message}
                {"  "}
                {msg.severity}
              </div>
            ))}
        </div>
      ) : null}
    </div>
  );
}

Snackbar.js

import { useState, useEffect } from "react";
import Alert from "@mui/material/Alert";
import Snackbar from "@mui/material/Snackbar";

export default function SnackBar(props) {
  const { snackbarData, setSnackbarData } = props;

  const [open, setOpen] = useState(false);
  const [timer, setTimer] = useState();

  // drop the first message, since it's just finished showing
  const removeSnackbarMessage = () => {
    console.info("removeSnackbarMessage");
    setSnackbarData((s) => s.filter((_i, key) => key > 0));
  };

  // close snackbar and empty messages (if force closed)
  const handleClose = (_event, reason) => {
    if (reason === "clickaway") {
      return;
    }
    setOpen(false);
    setSnackbarData([]);
  };

  // open or close the snackbar depending on if there is data.
  useEffect(() => {
    setOpen(!!snackbarData.length);
  }, [snackbarData]); // dont need .length as a dependency

  // whenever snackbar is toggled, initiate the queue and check on it.
  useEffect(() => {
    if (open) {
      console.info("SET NEW INTERVAL!");
      const interval = setInterval(() => {
        // no more data? stop the timer.
        if (!snackbarData.length) clearInterval(timer);
        removeSnackbarMessage();
      }, 4000);
      setTimer(interval);
    }
    return () => clearInterval(timer);
  }, [open]);

  // grab the first message in queue, if any exist
  const firstMsg = snackbarData.length ? snackbarData[0] : null;

  // show nothing
  if (!firstMsg) return null;

  return (
    <Snackbar
      open = {open}
      // autoHideDuration = {4000}
      anchorOrigin = {{ horizontal: "right", vertical: "bottom" }}
      onClose = {handleClose}
    >
      <Alert severity = {firstMsg?.severity}>{firstMsg?.message}</Alert>
    </Snackbar>
  );
}

пакет.json

{
  "name": "react",
  "version": "1.0.0",
  "description": "React example starter project",
  "keywords": [
    "react",
    "starter"
  ],
  "main": "src/index.js",
  "dependencies": {
    "@emotion/react": "^11.10.5",
    "@emotion/styled": "^11.10.5",
    "@mui/material": "^5.11.8",
    "react": "18.2.0",
    "react-dom": "18.2.0",
    "react-scripts": "^5.0.1",
    "typescript": "^4.9.5"
  },
  "devDependencies": {
    "@babel/runtime": "7.13.8"
  },
  "scripts": {
    "start": "react-scripts start",
    "build": "react-scripts build",
    "test": "react-scripts test --env=jsdom",
    "eject": "react-scripts eject"
  },
  "browserslist": [
    ">0.2%",
    "not dead",
    "not ie <= 11",
    "not op_mini all"
  ]
}

Рабочий кодSandbox:

https://codesandbox.io/p/sandbox/react18-mui5-queued-snackbar-messages-k9v6rw

Ожидаемая функциональность:

Первоначально он загружает 3 сообщения и воспроизводит их по 4 секунды за раз. Вы также можете поставить в очередь больше сообщений, просто нажав кнопку.

Надеюсь, это поможет!

Спасибо, только что добавил, все отлично работает! Очень хорошо сформулированный ответ :)

aabdulahad 08.02.2023 21:56

Другие вопросы по теме