facebook / react

The library for web and native user interfaces.
https://react.dev
MIT License
228.75k stars 46.82k forks source link

[React 19] Export SuspenseException #30993

Open cevek opened 1 month ago

cevek commented 1 month ago

Problem Statement

Currently, in React's use(promise) mechanism, there is no straightforward way to determine whether an exception originates from a promise suspended within the use hook. This makes it challenging for developers to:

Proposal

React should export either:

  1. A SuspenseException class that can be used to identify errors originating from a promise suspension, or
  2. A utility function to check whether a given error is caused by a suspended promise in use(promise).

Example Usage

  1. SuspenseException Class:

    import { SuspenseException } from 'react';
    
    try {
      use(fetchData());
    } catch (error) {
      if (error === SuspenseException) {
        // Handle Suspense-related logic
      } else {
        // Handle other types of errors
      }
    }
  2. Utility Function:

    import { isSuspenseException } from 'react';
    
    try {
      use(fetchData());
    } catch (error) {
      if (isSuspenseException(error)) {
        // Handle Suspense-related logic
      } else {
        // Handle other types of errors
      }
    }

Benefits

Conclusion

By exporting either a SuspenseException or a utility function, React would offer developers more control over managing Suspense-related errors, improving both error handling and debugging in applications.

artalar commented 1 month ago

Agree. Currently, the handling of suspense looks very unreliable, here is an example from a real code: https://github.com/artalar/reatom/blob/e6b042f273dbacb221ed77923f6c8582e22236dc/packages/npm-react/src/index.ts#L209-L210

eps1lon commented 1 month ago

Have you considered the suggestions in the exception?

Suspense Exception: This is not a real error! It's an implementation detail of use to interrupt the current render. You must either rethrow it immediately, or move the use call outside of the try/catch block. Capturing without rethrowing will lead to unexpected behavior.\n\nTo handle async errors, wrap your component in an error boundary, or call the promise's .catch method and pass the result to use

Which use case are these suggestions not covering?