@@ -2316,6 +2316,106 @@ describe('ReactHooksWithNoopRenderer', () => {
23162316 expect ( Scheduler ) . toFlushAndYieldThrough ( [ 'Unmount: 1' ] ) ;
23172317 expect ( ReactNoop . getChildren ( ) ) . toEqual ( [ ] ) ;
23182318 } ) ;
2319+
2320+ describe ( 'errors thrown in passive destroy function within unmounted trees' , ( ) => {
2321+ let BrokenUseEffectCleanup ;
2322+ let ErrorBoundary ;
2323+ let LogOnlyErrorBoundary ;
2324+
2325+ beforeEach ( ( ) => {
2326+ BrokenUseEffectCleanup = function ( ) {
2327+ useEffect ( ( ) => {
2328+ Scheduler . unstable_yieldValue ( 'BrokenUseEffectCleanup useEffect' ) ;
2329+ return ( ) => {
2330+ Scheduler . unstable_yieldValue (
2331+ 'BrokenUseEffectCleanup useEffect destroy' ,
2332+ ) ;
2333+ throw new Error ( 'Expected error' ) ;
2334+ } ;
2335+ } , [ ] ) ;
2336+
2337+ return 'inner child' ;
2338+ } ;
2339+
2340+ ErrorBoundary = class extends React . Component {
2341+ state = { error : null } ;
2342+ static getDerivedStateFromError ( error ) {
2343+ Scheduler . unstable_yieldValue (
2344+ `ErrorBoundary static getDerivedStateFromError` ,
2345+ ) ;
2346+ return { error} ;
2347+ }
2348+ componentDidCatch ( error , info ) {
2349+ Scheduler . unstable_yieldValue ( `ErrorBoundary componentDidCatch` ) ;
2350+ }
2351+ render ( ) {
2352+ if ( this . state . error ) {
2353+ Scheduler . unstable_yieldValue ( 'ErrorBoundary render error' ) ;
2354+ return 'ErrorBoundary fallback' ;
2355+ }
2356+ Scheduler . unstable_yieldValue ( 'ErrorBoundary render success' ) ;
2357+ return this . props . children ;
2358+ }
2359+ } ;
2360+
2361+ LogOnlyErrorBoundary = class extends React . Component {
2362+ componentDidCatch ( error , info ) {
2363+ Scheduler . unstable_yieldValue (
2364+ `LogOnlyErrorBoundary componentDidCatch` ,
2365+ ) ;
2366+ }
2367+ render ( ) {
2368+ Scheduler . unstable_yieldValue ( `LogOnlyErrorBoundary render` ) ;
2369+ return this . props . children ;
2370+ }
2371+ } ;
2372+ } ) ;
2373+
2374+ it ( 'should not error if the nearest unmounted boundary is log-only' , ( ) => {
2375+ function Conditional ( { showChildren} ) {
2376+ if ( showChildren ) {
2377+ return (
2378+ < LogOnlyErrorBoundary >
2379+ < BrokenUseEffectCleanup />
2380+ </ LogOnlyErrorBoundary >
2381+ ) ;
2382+ } else {
2383+ return null ;
2384+ }
2385+ }
2386+
2387+ act ( ( ) => {
2388+ ReactNoop . render (
2389+ < ErrorBoundary >
2390+ < Conditional showChildren = { true } />
2391+ </ ErrorBoundary > ,
2392+ ) ;
2393+ } ) ;
2394+
2395+ expect ( Scheduler ) . toHaveYielded ( [
2396+ 'ErrorBoundary render success' ,
2397+ 'LogOnlyErrorBoundary render' ,
2398+ 'BrokenUseEffectCleanup useEffect' ,
2399+ ] ) ;
2400+
2401+ act ( ( ) => {
2402+ ReactNoop . render (
2403+ < ErrorBoundary >
2404+ < Conditional showChildren = { false } />
2405+ </ ErrorBoundary > ,
2406+ ) ;
2407+ expect ( Scheduler ) . toFlushAndYieldThrough ( [
2408+ 'ErrorBoundary render success' ,
2409+ ] ) ;
2410+ } ) ;
2411+
2412+ expect ( Scheduler ) . toHaveYielded ( [
2413+ 'BrokenUseEffectCleanup useEffect destroy' ,
2414+ // This should call componentDidCatch too, but we'll address that in a follow up.
2415+ // 'LogOnlyErrorBoundary componentDidCatch',
2416+ ] ) ;
2417+ } ) ;
2418+ } ) ;
23192419 } ) ;
23202420
23212421 describe ( 'useLayoutEffect' , ( ) => {
0 commit comments