Skip to content

Fix/toast id theme context#267

Merged
Mosas2000 merged 31 commits intomainfrom
fix/toast-id-theme-context
Mar 14, 2026
Merged

Fix/toast id theme context#267
Mosas2000 merged 31 commits intomainfrom
fix/toast-id-theme-context

Conversation

@Mosas2000
Copy link
Copy Markdown
Owner

Closes #249

Preparation for replacing the module-level toast ID counter with a
ref-based counter scoped to each hook instance.
The module-level let counter persists across hot reloads and is
shared between multiple hook instances. Removing it in preparation
for a useRef-based replacement.
Each useToast instance now has its own counter via useRef. The ref
persists across renders but is scoped to the component lifecycle,
avoiding the issues with module-level state.
Replaces the module-level counter with the ref-based counter.
Each useToast instance now generates IDs independently, preventing
duplicate IDs if multiple instances are ever created.

Closes #249 (toast ID part)
Prevents a potential crash if Toast is rendered without an onClose
handler during development or testing.
Screen readers will now announce each toast as an alert when it
appears, improving accessibility for notifications.
Makes it easier to target the toast container in integration tests.
The label now reads 'Dismiss success notification' etc., giving
screen reader users more context about which notification they
are dismissing.
The fade-out-then-remove logic was duplicated in the timer effect
and the close button handler. Extracting it into a single dismiss
callback wrapped in useCallback ensures consistent behavior.
The fixed toast container could intercept clicks on page elements
behind it. Setting pointer-events-none on the container and
pointer-events-auto on individual toasts ensures only visible
toasts capture interaction.
getInitialTheme now returns 'light' when window is undefined,
preventing crashes in server-side rendering or pre-rendering
environments.

Closes #249 (ThemeContext part)
localStorage.getItem() can throw in private browsing mode on some
browsers. matchMedia can throw in restricted environments. Wrapping
both in try/catch ensures getInitialTheme always returns a valid
theme string.
If localStorage is unavailable (e.g. quota exceeded, private
browsing), the setItem call would throw and break the theme toggle.
Silently catching keeps the toggle functional even without
persistence.
Prevents errors when running in environments where document is
not defined, such as Node.js during SSR.
React DevTools will now show 'ThemeContext' instead of 'Context'
in the component tree, making debugging easier.
Test files can now import ThemeContext directly to verify provider
values without relying on the useTheme hook.
Preparation for memoizing the context value and the toggle
callback to prevent unnecessary re-renders of consumers.
The toggle function identity is now stable across renders, which
prevents unnecessary re-renders of components that depend on it.
Without useMemo the provider creates a new value object on every
render, causing all consumers to re-render even when the theme
has not changed. Memoizing on [theme, toggleTheme] fixes this.
Test files can now import and call getInitialTheme directly to
verify the SSR guard and localStorage fallback logic.
Consumers that only need to know if dark mode is active can use
isDark instead of comparing theme === 'dark' repeatedly.
Allows consumers to programmatically set a specific theme rather
than only toggling. Useful for settings pages or preset themes.
Avoids repeating the string literal in multiple places. If the
key ever needs to change, it only needs to be updated once.
@Mosas2000 Mosas2000 merged commit 0178dcd into main Mar 14, 2026
2 of 6 checks passed
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.

Toast ID counter and ThemeContext localStorage are fragile under edge conditions

1 participant