Skip to content

Subscribe to state changes earlier, with useLayoutEffect #624

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 3 commits into
base: main
Choose a base branch
from

Conversation

tjenkinson
Copy link

@tjenkinson tjenkinson commented Apr 27, 2025

Thanks for the great library!

I wanted to show an error toast using toast.error() from inside a useLayoutEffect, but it didn't work.

This fixes that 🎉

So now providing the useLayoutEffect is from a component rendered after <Sonner /> it should work, and this also means that using useEffect anywhere should always work regardless of whether <Sonner /> is before or after the component using the hook.

Related #168

I also spotted a few race conditions so added some fixes for those in here too

I tried to run the tests locally but npm run dev doesn't seem to start the web server for some reason, not sure why

so that `useEffect` calls can emit toasts on the initial render
`useEffect` won't run the callback on the server, so it should always be there?
Copy link

vercel bot commented Apr 27, 2025

The latest updates on your projects. Learn more about Vercel for Git ↗︎

Name Status Preview Comments Updated (UTC)
sonner ✅ Ready (Inspect) Visit Preview 💬 Add feedback Apr 27, 2025 4:00pm

@@ -1,5 +1,7 @@
import React from 'react';

export const safeUseLayoutEffect = typeof window !== 'undefined' ? React.useLayoutEffect : () => {};
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

neither useEffect or useLayoutEffect will run on the server, but useLayoutEffect will log a warning on the server, hence this

Comment on lines +194 to +201
React.useEffect(() => {
if (removed) {
const timeout = setTimeout(() => {
removeToastRef.current(toast);
}, TIME_BEFORE_UNMOUNT);
return () => clearTimeout(timeout);
}
}, [removed]);
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

moved here so that the timer is cleared if component torn down

@@ -690,7 +717,6 @@ const Toaster = React.forwardRef<HTMLElement, ToasterProps>(function Toaster(pro
}
}

if (typeof window === 'undefined') return;
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

shouldn't be needed given inside a useEffect, which won't run on the server?

@tjenkinson tjenkinson marked this pull request as ready for review April 27, 2025 16:02
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

1 participant