GithubHelp home page GithubHelp logo

Comments (16)

kentcdodds avatar kentcdodds commented on May 17, 2024 6

We're implementing a new fallbackRender prop which will allow you to do whatever you'd like:

<ErrorBoundary fallbackRender={props => <YourErrorFallback {...props} your="extra props" />}>
  ...
</ErrorBoundary>

from react-error-boundary.

bvaughn avatar bvaughn commented on May 17, 2024 4

What stops you from passing an inline function component currently?

const _Uploader = lazy(() => import("uploader"));

type Props = { onRequestClose: () => void };
const Uploader: SFC<Props> = props => (
  <ErrorBoundary
    FallbackComponent={({ error }) => (
      <MyErrorComponent error={error} onRequestClose={onRequestClose} />
    )}
  >
    <Suspense fallback={<MyLoadingComponent {...props} />}>
      <_Uploader {...props} />
    </Suspense>
  </ErrorBoundary>
);

Here's an example:
https://codesandbox.io/s/zk469wp6jp

from react-error-boundary.

bvaughn avatar bvaughn commented on May 17, 2024 2

This is a very nuanced topic. As I mentioned in my comment right above, I've discussed this in depth on a recent GitHub issue. Rather than repeat it all, here you go:

bvaughn/react-window#85

from react-error-boundary.

OliverJAsh avatar OliverJAsh commented on May 17, 2024

Wouldn't that have poor performance? The fallback component reference will be changing identity with every render of Uploader, which would cause it to remount unnecessarily?

Render props don't have that issue as they bypass calling React.createElement.

from react-error-boundary.

bvaughn avatar bvaughn commented on May 17, 2024

Creating an inline function is fast. People do that in tons of places. Rendering ErrorBoundary is fast too. Unless there's an error, it just passes through props.children.

But if you have concerns about recreating this component, you can memoize it 😄

https://codesandbox.io/s/w7v038y1r5

from react-error-boundary.

OliverJAsh avatar OliverJAsh commented on May 17, 2024

My concern is not about creating functions, but rather about React remounting a component needlessly (because it's reference identity is changing)—unless I misunderstand.

Thanks for the memoizing example, that makes sense. Unfortunately it's awkward because every time message changes, we'll create a whole new component, which will lead to a remount, when it should really only lead to an update of the same component.

My concern about remounts isn't just about performance, but also any DOM state, like input values or focus. Remounting will lose/reset that state?

from react-error-boundary.

bvaughn avatar bvaughn commented on May 17, 2024

Render props don't have that issue as they bypass calling React.createElement.

I don't think your real complaint here is with render props vs React elements. I think it's with the ability to pass arbitrary custom props along.

Either way render props are called (as vanilla functions or wrapped in createElement) you would need a wrapper with inline scope, or a memoized approach like I linked above, to pass additional, custom props. I don't really feel strongly about not supporting custom props with ErrorComponent, but I also don't think it's necessary. This component is a lightweight example of how an Error Boundary can work. It's not all that useful in itself. 😄

But as far as render props (functions) vs React elements, I think React elements are a more useful pattern because they are more flexible for users. (You can pass a function component or a class component, your component can use "hooks", etc.) They also fit into React's concurrent rendering model a bit better, since React can decide if it should process the components now or wait until the next frame.

from react-error-boundary.

bvaughn avatar bvaughn commented on May 17, 2024

Unfortunately it's awkward because every time message changes, we'll create a whole new component

I think this is micro-optimizing. How expensive is this "whole new component"? It's really just a tiny function that returns a React element. It's like recreating an inline click event handler.

which will lead to a remount, when it should really only lead to an update of the same component.

Why would it lead to a remount? The only thing that would remount in this case would be your error message. If that's shown, your app is in a failure state. (And also the inline message prop seems unlikely to actually change in this case.)

from react-error-boundary.

OliverJAsh avatar OliverJAsh commented on May 17, 2024

only thing that would remount in this case would be your error message

That was my understanding.

Re. performance, I agree! However I think the point about losing DOM state (e.g. focus on an inner button) due to remounts is more worrying, as that could be bad UX/accessibility—albeit it's quite unlikely you'll have much going on inside of an error fallback component, but ideally we wouldn't have to make that assumption.

from react-error-boundary.

bvaughn avatar bvaughn commented on May 17, 2024

However I think the point about losing DOM state (e.g. focus on an inner button) due to remounts is more worrying

Just to be clear, the children you wrap inside of your error boundary- in the normal (non-errored) state– would not remount or be impacted by this. The only thing that would remount would be your fallback UI, in which case...your app is broken. It can't recover. (The ErrorBoundary component in this lib doesn't provide a way for you to reset its state and render non-fallback UI once it's been triggered.) So I'm still not understanding what the concrete concern is here.

I think fallback components are usually static overlays, but...assuming you had some form elements in there that were stateful– under what conditions would your onRequestClose prop change to recreate the fallback component? I feel like we're talking about a very unlikely hypothetical here.

from react-error-boundary.

OliverJAsh avatar OliverJAsh commented on May 17, 2024

under what conditions would your onRequestClose prop change to recreate the fallback component?

Wouldn't the fallback component also be recreated every time Uploader is called? And that could happen any number of times (with each re-render of the parent), assuming Uploader is not memoized.

If Uploader was memoized (using PureComponent or React.memo), my understanding is that the fallback component would only be recreated if the props changed. In this case, that's just onRequestClose.

Is my understanding correct? If so, the former scenario seems not so unlikely to me. Of course it's easy to make it memoized, but is that an assumption/requirement we want to bake in to this library?

(Thanks for helping me understand this!)

from react-error-boundary.

bvaughn avatar bvaughn commented on May 17, 2024

Wouldn't the fallback component also be recreated every time Uploader is called? And that could happen any number of times (with each re-render of the parent), assuming Uploader is not memoized.

Why would this matter? Again, this "component" is a very small function, not unlike a click handler. Recreating it is fast.

Thanks for helping me understand this!

No problem. It's an interesting discussion. :)

from react-error-boundary.

OliverJAsh avatar OliverJAsh commented on May 17, 2024

If the fallback component is recreated (fast so not a problem in itself), it will be remounted (fast so not a problem in itself). In turn, the remount would mean any DOM state of the fallback component will be lost (e.g. an inner input's value or a button's focus). This is my concern.

Initially I thought recreating + remounting components needlessly was a performance issue, but the more I've given it thought—and after you clarified the situation as negligible—now my hesitation is around trying to avoid bad UX or accessibility issues.

Of course there are many scenarios where this won't be an issue (most of the time, we presume the fallback component will be a simple error message).

from react-error-boundary.

bvaughn avatar bvaughn commented on May 17, 2024

It's funny because I'm having a similar conversation in another repo with someone else this morning (bvaughn/react-window/issues/85)

In this case of this specific library, I don't feel very strongly about any particular API decision. I didn't think this component would get much use when I created it. I thought it would just be an example to reference.

If you'd like to submit a PR that changes the component to pass through props in a way similar to this, I wouldn't really object:

  render() {
    const {children, FallbackComponent, ...rest} = this.props;
    const {error, info} = this.state;

    if (error !== null) {
      return (
        <FallbackComponent
          {...rest}
          componentStack={
            // istanbul ignore next: Ignoring ternary; can’t reproduce missing info in test environment.
            info ? info.componentStack : ''
          }
          error={error}
        />
      );
    }

    return children;
  }

from react-error-boundary.

chochihim avatar chochihim commented on May 17, 2024

Sorry to interrupt. I have opened an issue on reactjs.org to seek for clarification on the render props documentation. I was told (and convinced) that render props is a better pattern because that would not trigger re-mount when inlining (see example at reactjs/react.dev#1241 (comment)). But reading your discussion makes me feel the opposite.

Is there no universally better pattern? Can you shed some light on this?

from react-error-boundary.

kentcdodds avatar kentcdodds commented on May 17, 2024

🎉 This issue has been resolved in version 2.0.0 🎉

The release is available on:

Your semantic-release bot 📦🚀

from react-error-boundary.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.