Issue
It seems the useEffect hook in the useMessage hook is the cause of all the trouble, specifically its interaction with the hide function.
const hide = () => {
setIsMessageShown(false);
};
...
useEffect(() => {
if (message && severity) {
setIsMessageShown(true);
}
return () => {
if (isMessageShown === false) {
setMessage(null);
setSeverity(null);
}
};
}, [
message,
setMessage,
severity,
setSeverity,
isMessageShown,
setIsMessageShown
]);
When hide is called it enqueues a state update to toggle the isMessageShown state false. isMessageShown is a dependency for the useEffect and there's a condition that checks if there's a truthy message and severity state. There is, since that wasn't cleared out too, so another state update is enqueued to toggle the isMessageShown state back true. At the same time, the useEffect hook is returning a cleanup function then enqueues state updates to clear the message and severity state, but the Snackbar and Alert components are still displayed.
There appears to be a state synchronicity issue from here on out and this is why the alert doesn't work the same after the first invocation.
Solution
Commenting out, or removing, the useEffect hook appears to entirely resolve the issue you describe and reproduce in the codesandbox.
Here's an updated useMessage hook.
import { usePanel } from "./PanelContext";
const useMessage = () => {
const { setIsMessageShown, setMessage, setSeverity } = usePanel();
const show = (data, action, type) => {
if (data && data.message) {
data = data.message;
}
setMessage(data);
setSeverity(type);
setIsMessageShown(true);
};
const success = (data, action) => {
show(data, action, "success");
};
const info = (data, action) => {
show(data, action, "info");
};
const warning = (data, action) => {
show(data, action, "warning");
};
const error = (data, action) => {
show(data, action, "error");
};
const hide = () => {
setIsMessageShown(false);
};
return {
success,
info,
warning,
error,
show,
hide
};
};
export default useMessage;
I did a bit of refactoring to centralize the Panel context code & logic
PanelContext.js
import React, { useContext, useState } from "react";
export const PanelContext = React.createContext();
export const usePanel = () => useContext(PanelContext);
const PanelProvider = ({ children }) => {
const [isMessageShown, setIsMessageShown] = useState();
const [message, setMessage] = useState();
const [severity, setSeverity] = useState();
return (
<PanelContext.Provider
value={{
isMessageShown,
setIsMessageShown,
message,
setMessage,
severity,
setSeverity
}}
>
{children}
</PanelContext.Provider>
);
}
export default PanelProvider;
