skip to Main Content

I tried to find a solution with GPT for a while, but didn’t get a good result.

The Basis:
I have built a snack bar to display errors. This takes an error as a string parameter and saves it in a list. This list is then displayed at the top right. Every single message should disappear after 7 seconds, or when you press the close button.
List of errors

The Problem:
(The following description is for two messages that should be displayed one after the other, but errors occur with all possible combinations)
After seven seconds, both messages disappear briefly and one reappears immediately and then remains without a time limit until it is closed using the button.

Expected behaviour:
The first message should disappear after 7 seconds, the second shortly afterwards (because it was also displayed a little later) should also disappear. Closing via the button should still always be possible.

The close button has always worked without any problems until now.

The Code:

  const Snackbar = ({ newMessage }: { newMessage: string }) => {
  const [messages, setMessages] = useState<string[]>([]);

  const handleClickClose = (messageToRemove: string) => {
    setMessages(messages.filter((message) => message !== messageToRemove));
  };

  if (newMessage !== '') {
     if (newMessage !== messages[messages.length - 1]) {
        setMessages([...messages, newMessage]);
        setTimeout(handleClickClose, 7000, newMessage);
     }
  }

  useEffect(() => {}, [messages]);

I left out the whole return statement with the tailwind styling, I didn’t think it was relevant, it just iterates over the messages list with .map and styles the individual messages a bit

My idea behind the code:
The handleClickClose function should be understandable and works.

after that was the idea -> I check if the new message is filled at all -> if the new message is not already the last one in the list, so that the same message is not displayed twice in a row. -> if this is not the case, I add the message to the list and set the timer so that it calls the handleCloseClick function with this new message after 7 seconds.

The useEffect should ensure that when the setTimeout function or the close button changes the list, the component is reloaded

Thanks for the help in advance

2

Answers


  1. Chosen as BEST ANSWER

    Thanks for the help everybody. In the end i got it working with the help of useRef from react. This will show a list of message where every message has a lifetime of 6 seconds and can be closed by a button. Here is the code:

    type Message = {
      id: number;
      text: string;
    };
    
    interface SnackbarProps {
      newMessage: string;
    }
    
    const Snackbar: React.FC<SnackbarProps> = ({
      newMessage,
    }: {
      newMessage: string;
    }) => {
    const [messages, setMessages] = useState<Message[]>([]);
    const timersRef = useRef<NodeJS.Timeout[]>([]);
    
    useEffect(() => {
      if (newMessage !== '') {
        const newMessageObject = {
          id: Date.now(),
          text: newMessage,
        };
        setMessages([...messages, newMessageObject]);
          const timerId = setTimeout(() => {
          removeMessage(newMessageObject.id);
          timersRef.current = timersRef.current.filter((id) => id !== timerId);
        }, 6000);
    
        timersRef.current.push(timerId);
      }
      // eslint-disable-next-line react-hooks/exhaustive-deps
    }, [newMessage]);
    
    const removeMessage = (id: number) => {
      setMessages((messages) => messages.filter((message) => message.id !== id));
    };
    
    useEffect(() => {
      return () => {
        timersRef.current.forEach(clearTimeout);
      };
    }, []);
    

  2. When you set timeout to close snackbar, function run after 7s with data which is save from time of call function.
    You should change your code to this:

    const Snackbar = ({ newMessage }: { newMessage: string }) => {
        const [messages, setMessages] = useState<string[]>([]);
        const [messageToRemove, setMessageToRemove] = useState<string>('');
    
    const handleClickClose = (message: string) => {
        setMessageToRemove(message);
    };
    
    useEffect(() => {
        if (newMessage !== '') {
            if (newMessage !== messages[messages.length - 1]) {
                setMessages([...messages, newMessage]);
                setTimeout(handleClickClose, 7000, newMessage);
            }
        }
    }, [newMessage])
    
    useEffect(() => {
        setMessages(messages.filter((message) => message !== messageToRemove));
    }, [messageToRemove]);
    
    return ();
    }
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search