I'm trying to build a toaster component with React's hook api but I can't figure out how to make it behave correctly. Right now all toasts disappear, 1 is removed, the rest appears and the cycle repeats.
Ideally the oldest Toast notification disappears while the rest is clearly visible.
Link to REPL: https://repl.it/@nikolaipaul/FabulousHideousMicrobsd
Here is the code for the Toast component:
const Toast = ({ id, message, remove }) => {
const [isShown, setIsShown] = React.useState(true)
React.useEffect(() => {
if (isShown) {
const timeout = setTimeout(() => {
console.log(`hiding toast id ${id}`)
setIsShown(false)
}, 2000)
return () => clearTimeout(timeout)
}
})
return (
<CSSTransition
classNames="toast"
unmountOnExit
mountOnEnter
appear
in={isShown}
timeout={300}
onExited={() => remove(id)}>
<div className="toast">{message}</div>
</CSSTransition >
)
}
and the hook that manages the list of toasts:
export const useToast = () => {
const [toasts, setToasts] = React.useState([])
const add = message => {
const id = Math.random().toString(36).substr(2, 9)
const toast = {
id,
message,
remove,
}
console.log(`adding toast id ${id}`)
setToasts(toasts => toasts.concat(toast))
}
const remove = id => {
console.log(`removing toast id ${id}`)
setToasts(toasts => toasts.filter(toast => toast.id !== id))
}
const Toaster = () => {
const toaster =
<div style={{ position: "fixed", right: 0, top: 0 }}>
{toasts.map(toast => <Toast {...toast} key={toast.id} />)}
</div>
return ReactDOM.createPortal(toaster, document.getElementById("toasts"))
}
return [Toaster, add]
}