mui / mui/base-ui

[toast] useToastManager() causes unnecessary re-renders for action-only consumers

Open
#4,234 1 comment 0 reactions 0 assignees View on GitHub
component: toast type: new feature
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

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.