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.

Drew Reese
  • 165,259
  • 14
  • 153
  • 181
user5711656
  • 3,310
  • 4
  • 33
  • 70
  • What does `addMessage`'s code look like? Please update your question with a [mcve] 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](http://meta.stackoverflow.com/questions/338537/). – T.J. Crowder Feb 03 '23 at 09: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](https://robinpokorny.com/blog/index-as-a-key-is-an-anti-pattern/) linked from [the documentation](https://reactjs.org/docs/lists-and-keys.html#keys). – T.J. Crowder Feb 03 '23 at 10:00
  • *here is my code https://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. – T.J. Crowder Feb 03 '23 at 10:08

2 Answers2

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

Gabriele Petrioli
  • 191,379
  • 34
  • 261
  • 317
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);
  };
gilamran
  • 7,090
  • 4
  • 31
  • 50