Я использую библиотеку React MUI с NextJS.
Я пытаюсь создать функцию, в которой сгенерированные закусочные хранятся в массиве в магазине (используя избыточную библиотеку под названием Easy-Peasy), а затем отображаются один за другим по истечении времени ожидания.
Например. если есть 3 закусочные из-за трех ошибок пользователя - то:
Сначала появится закусочная, а затем тайм-аут через 4 секунды.
Появится секунда, а затем 4-секундный тайм-аут
Третий будет отображаться, а затем тайм-аут через 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
.
Любая помощь приветствуется. ТИА.
Проблема в том, что сообщения, которые вы хотите «поставить в очередь», просто перезаписывают одно сообщение, хранящееся в состоянии. Вам нужно создать очередь (массив) сообщений, но в этом случае нельзя использовать свойство по умолчанию 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 секунды за раз. Вы также можете поставить в очередь больше сообщений, просто нажав кнопку.
Надеюсь, это поможет!
Спасибо, только что добавил, все отлично работает! Очень хорошо сформулированный ответ :)