From cd32bac9e2815262d5710053eee86a7528649607 Mon Sep 17 00:00:00 2001 From: Rob Snow Date: Wed, 8 Feb 2023 12:50:24 -0800 Subject: [PATCH 1/3] Fix React StrictMode Toast --- packages/@react-spectrum/toast/src/ToastContainer.tsx | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/packages/@react-spectrum/toast/src/ToastContainer.tsx b/packages/@react-spectrum/toast/src/ToastContainer.tsx index 8532ee24565..92911eb08b2 100644 --- a/packages/@react-spectrum/toast/src/ToastContainer.tsx +++ b/packages/@react-spectrum/toast/src/ToastContainer.tsx @@ -73,10 +73,9 @@ export function ToastContainer(props: SpectrumToastContainerProps): ReactElement // We use a ref to do this, since it will have a stable identity // over the lifetime of the component. let ref = useRef(); - toastProviders.add(ref); - // eslint-disable-next-line arrow-body-style useEffect(() => { + toastProviders.add(ref); return () => { // When this toast provider unmounts, reset all animations so that // when the new toast provider renders, it is seamless. From 34fcf0398dbf4319d208074453ac170cb8fc5a0a Mon Sep 17 00:00:00 2001 From: Rob Snow Date: Thu, 2 Mar 2023 09:55:48 -0800 Subject: [PATCH 2/3] Move to earlier effect --- packages/@react-spectrum/toast/package.json | 1 + packages/@react-spectrum/toast/src/ToastContainer.tsx | 6 +++++- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/packages/@react-spectrum/toast/package.json b/packages/@react-spectrum/toast/package.json index 4347b032680..3a0584a5209 100644 --- a/packages/@react-spectrum/toast/package.json +++ b/packages/@react-spectrum/toast/package.json @@ -39,6 +39,7 @@ "@react-aria/focus": "^3.11.0", "@react-aria/i18n": "^3.7.0", "@react-aria/toast": "3.0.0-alpha.1", + "@react-aria/utils": "^3.15.0", "@react-spectrum/button": "^3.12.0", "@react-spectrum/utils": "^3.9.0", "@react-stately/toast": "3.0.0-alpha.1", diff --git a/packages/@react-spectrum/toast/src/ToastContainer.tsx b/packages/@react-spectrum/toast/src/ToastContainer.tsx index 1cd050563c5..bb7b4ca16f1 100644 --- a/packages/@react-spectrum/toast/src/ToastContainer.tsx +++ b/packages/@react-spectrum/toast/src/ToastContainer.tsx @@ -15,6 +15,7 @@ import React, {ReactElement, ReactNode, useEffect, useRef} from 'react'; import {SpectrumToastValue, Toast} from './Toast'; import {Toaster} from './Toaster'; import {ToastOptions, ToastQueue, useToastQueue} from '@react-stately/toast'; +import {useLayoutEffect} from '@react-aria/utils'; import {useSyncExternalStore} from 'use-sync-external-store/shim/index.js'; export interface SpectrumToastContainerProps extends AriaToastRegionProps {} @@ -74,8 +75,11 @@ export function ToastContainer(props: SpectrumToastContainerProps): ReactElement // over the lifetime of the component. let ref = useRef(); - useEffect(() => { + useLayoutEffect(() => { toastProviders.add(ref); + }, [toastProviders, ref]); + + useEffect(() => { return () => { // When this toast provider unmounts, reset all animations so that // when the new toast provider renders, it is seamless. From 273b4bb118a0697df98e6f0a8b78190823164003 Mon Sep 17 00:00:00 2001 From: Rob Snow Date: Thu, 2 Mar 2023 15:30:58 -0800 Subject: [PATCH 3/3] fix implementation --- .../toast/src/ToastContainer.tsx | 27 ++++++++++++++----- 1 file changed, 21 insertions(+), 6 deletions(-) diff --git a/packages/@react-spectrum/toast/src/ToastContainer.tsx b/packages/@react-spectrum/toast/src/ToastContainer.tsx index bb7b4ca16f1..4fe560c0671 100644 --- a/packages/@react-spectrum/toast/src/ToastContainer.tsx +++ b/packages/@react-spectrum/toast/src/ToastContainer.tsx @@ -49,8 +49,25 @@ export function clearToastQueue() { globalToastQueue = null; } -let toastProviders = new Set(); + +let store = new Set(); +let toastProviders = { + add: (ref) => { + store.add(ref); + emitChange(); + }, + delete: (ref) => { + store.delete(ref); + emitChange(); + }, + values: () => store.values() +}; let subscriptions = new Set<() => void>(); +function emitChange() { + for (let fn of subscriptions) { + fn(); + } +} function subscribe(fn: () => void) { subscriptions.add(fn); return () => subscriptions.delete(fn); @@ -70,7 +87,8 @@ function useActiveToastContainer() { */ export function ToastContainer(props: SpectrumToastContainerProps): ReactElement { // Track all toast provider instances in a set. - // Only the first one will actually render. + // All will render null at first. Once they have been registered, all will re-render + // and the last one will become the active toast provider, the rest will still return null. // We use a ref to do this, since it will have a stable identity // over the lifetime of the component. let ref = useRef(); @@ -87,13 +105,10 @@ export function ToastContainer(props: SpectrumToastContainerProps): ReactElement toast.animation = null; } - // Remove this toast provider, and call subscriptions. + // Remove this toast provider. // This will cause all other instances to re-render, // and the first one to become the new active toast provider. toastProviders.delete(ref); - for (let fn of subscriptions) { - fn(); - } }; }, []);