0

I am trying to show multiple message using provider and hooks . But I am not able to show multiple message .One one message is show at one time don't know why ?

here is my code https://codesandbox.io/s/new-mountain-cnkye5?file=/src/App.tsx:274-562

React.useEffect(() => {
    setTimeout(() => {
      utilContext.addMessage("error 2 sec");
    }, 300);

    setTimeout(() => {
      utilContext.addMessage("error 5 mili sec");
    }, 2000);

    setTimeout(() => {
      utilContext.addMessage("error 1  sec");
    }, 1000);
  }, []);

I am also using map function to render all message.

 return (
    <>
      {messages.map((msg, index) => (
        <div key={`Toast-Message-${index}`}>
          {msg.msg}
          <button
            onClick={(event) => {
              alert("000");
            }}
          >
            close
          </button>
        </div>
      ))}

      <ConfirmationDialogContext.Provider value={value}>
        {children}
      </ConfirmationDialogContext.Provider>
    </>
  );

Expected output : It will show 3 messages after some time.

3
  • What does addMessage's code look like? Please update your question with a minimal reproducible example demonstrating the problem, ideally a runnable one using Stack Snippets (the [<>] toolbar button). Stack Snippets support React, including JSX; here's how to do one. Commented Feb 3, 2023 at 9:58
  • 1
    Also, beware of using an index as a key if the array changes. It works if the array only ever grows without previous elements changing, but doesn't work correctly if you remove elements from the array or change them. More in this post linked from the documentation. Commented Feb 3, 2023 at 10:00
  • here is my code codesandbox.io/s/new-mountain-cnkye5?file=/src/App.tsx:274-562" The way SO works, your whole question (including any necessary code) has to be in your question, not just linked. Three reasons: People shouldn't have to go off-site to help you; some sites are blocked for some users; and links rot, making the question and its answers useless to people in the future. Please put all necessary code in the question. Commented Feb 3, 2023 at 10:08

2 Answers 2

1

Change your

const addMessage = (message: string, status: "success" | "error") => {
    setmessages([...messages, { msg: message, type: status, duration: 5000 }]);
  };

to

  const addMessage = (message: string, status: "success" | "error") => {
    setmessages((currentMessages) => [
      ...currentMessages,
      { msg: message, type: status, duration: 5000 }
    ]);
  };

This is because you call the 3 addMessage in the same time, and so the messages variable has the same value in all three calls.

Read Updating state based on the previous state for more info on this syntax

Sign up to request clarification or add additional context in comments.

1 Comment

There are plenty of previous questions and answers covering stale state.
0

You context is changing every time you add a message, and in your useEffect dependencies you don't have the context as a dep. which means that you're only adding messages to the first context instance, while rendering the latest one all the time.

BUT, if you add the context to your useEffect dependencies you will get an infinite loop.

One (Bad) solution would be to track every setTimeout with useRef like this:

export default function App() {
  const utilContext = useConfirmationDialog();
  const m1Ref = React.useRef(false);
  const m2Ref = React.useRef(false);
  const m3Ref = React.useRef(false);

  React.useEffect(() => {
    console.log("useEffect");
    const s = [];
    if (m1Ref.current === false) {
      const s1 = setTimeout(() => {
        utilContext.addMessage("error 300 msec");
      }, 300);
      s.push(s1);
      m1Ref.current = true;
    }
    
    if (m2Ref.current === false) {
      const s2 = setTimeout(() => {
        m2Ref.current = true;
        utilContext.addMessage("error 2 sec");
      }, 2000);
      s.push(s2);
    }
    
    if (m3Ref.current === false) {
      const s3 = setTimeout(() => {
        m3Ref.current = true;
        utilContext.addMessage("error 1  sec");
      }, 1000);
      s.push(s3);
    }

    return () => {
      s.forEach((x) => clearTimeout(x));
    };
  }, [utilContext]);
  
  return (
    <Typography>
      MUI example. Please put the code to reproduce the issue in src/App.tsx
    </Typography>
  );
}

I think that you should move the delay of presenting the messages out of the sender into the context, like this:

  const addMessage = (delay: number, message: string, status: "success" | "error") => {
    setTimeout(() => {
      setmessages((currentMessages) => [
        ...currentMessages,
        { msg: message, type: status, duration: 5000 }
      ]);
    }, delay);
  };

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.