timolins / react-hot-toast

Smoking Hot React Notifications 🔥
https://react-hot-toast.com
MIT License
9.84k stars 331 forks source link

Hanlders are not memoized #243

Closed ignaciomartinelias closed 1 year ago

ignaciomartinelias commented 1 year ago

I've been using react-hot-toast in my project for a while now. Since I'm using a custom toast with tailwind, I created my own useToastNotificatiion hook that encapsulates my logic. Unfortunately, I'm unable to trigger the toast within any useEffect because I'm using handlers (startPause and endPause) to prevent the dismissal of the toast whenever the user hovers over the toast itself. This is because the handlers are not memorized. This causes the useEffect to trigger over and over again even if I wrap my custom triggerToast function in a useCallback because the handlers rebuild my function over and over again. If only these hanlders would be memorized then we could use them without any issues. Here's an example of how I would use my code.

const useToastNotification = () => {
  const { handlers } = useToaster()
  const { startPause, endPause } = handlers
}

const triggerToast = useCallback(({
      message,
      variant,
      onUndo,
      hasIcon,
      hideCloseButton = false,
      duration
    }) => toast.custom(
      t => (
        <ToastBar toast={t} style={clearDefaultStyles}>
          {() => (
              {t.visible && (
                <div
                  onMouseEnter={startPause}
                  onMouseLeave={endPause}
                >
                  <ToastNotifications // This is my custom toast
                    variant={variant}
                    message={message}
                    onUndo={onUndo}
                    onClose={
                      !hideCloseButton ? () => toast.dismiss(t.id) : undefined
                    }
                    hasIcon={hasIcon}
                  />
                </div>
              )}
          )}
        </ToastBar>
      ),
      { duration }
    )
}, [startPause, endPause])

return triggerToast
// Some component
...

const triggerToast = useToastNotification()

useEffect(() => {
  if(isError) {
    triggerToast()
    resetError()
  }
}, [triggerToast, isError, resetError])

// The resetError function is correctly memorized so it always points to the same reference
// The isError is just a boolean so no problems here
// The triggerToast function gets recreated on every rerender even though I'm using useCallback because `endPause` and `startPause` get recreated over and over again