[toast] useToastManager() causes unnecessary re-renders for action-only consumers
- Dominant language
- TypeScript
- Stars
- 10.9k
- Forks
- 543
- Avg merge
- 1d 20h
- Merged PRs (30d)
- 101
Description
## Summary
Components that call `useToastManager()` solely to fire toasts (using `add`, `close`, `update`, `promise`) re-render on **every** toast state change — even though they never read the `toasts` array.
## Reproduction
```tsx
function SaveButton() {
const { add } = Toast.useToastManager();
return add({ title: 'Saved!' })}>Save;
}
```
This component re-renders every time **any** toast is added, closed, or times out — anywhere in the app.
## Root cause
`useToastManager()` unconditionally calls `store.useState('toasts')`, which registers a `useSyncExternalStore` subscription. Every mutation produces a new array reference, so `Object.is(oldToasts, newToasts)` is always `false` and the component re-renders.
The action methods (`addToast`, `closeToast`, etc.) are stable references — arrow functions on the `ToastStore` class instance. But the subscription is registered before the caller destructures the return value, so destructuring only `{ add }` doesn't help.
## Expected behavior
Components that only need to **dispatch** toast actions should **not** re-render when toast state changes. Only components that **read** `toasts` (to display them) should subscribe and re-render.
## Proposed solution
Add a `useToastActions()` hook that returns only the stable action methods without subscribing to state.
PR: #4233
Contributor guide
Assessment
This issue has not been assessed yet.