-
-
Notifications
You must be signed in to change notification settings - Fork 407
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
componentDidCatch - preventing the app from breaking when a rendering problem occurs #518
Comments
any rendering error should be caught at compile time. For runtime errors, there is the |
@webark a rendering error can't be caught at compile time if there is a conditional render. |
should we have it on component level? <div {{on-render-error this.onError}}>
....
</div> {{#each this.items as |item|}}
<MyListItem {{on-render-error (fn this.catchListItemRenderError item)}} />
{{/each}} |
I think something like that could be a nice thing to provide, and maybe the default route template is wrapped in a tagless version of that. Ignore the rest of this comment, I made the mistake of using a personal ideal API, which uses code from an unmerged RFC, #499. Ultimately, I think an API like: import Route from '@ember/routing/route';
import FooComponent from './components/Foo';
import ErrorComponent from 'my-app/components/generic-error';
import LoadingComponent from 'my-app/components/spinner';
export default class extends Route {
static render = FooComponent;
static loading = LoadingComponent;
static error = ErrorComponent;
async model() {
const contacts = await this.store.findAll('contact');
return { contacts };
}
} where if an error happens anywhere in the tree of |
I’d favor this being a modifier rather than adding api surface to routes. It gives you the flexibility of catching errors in confined areas or segments and IMO that’s a lot more power with less side effects and complexity. |
I def don't want to do that. I'd prefer 0 api surface changes. Just adding behavior to the existing api surface (my sample showed code proposed in RFC #499) |
I think we need more declarative way for root components, like export default class extends Route {
static states = {
loading: FooLoading,
error: FooLoadingError,
pending: FooPendingUserAction,
success: FooListItems
}
private currentState = 'pending';
@task
model(transition) {
try {
const data = yield fetch('data.json')
this.router.currentRoute.switchTo('succes', transition, data);
} catch (e) {
transition.abort();
this.router.currentRoute('pending', new Transition('....') , { error: e });
}
}
} |
I think that's out of scope for this proposed RFC. taking from this page: https://guides.emberjs.com/release/routing/loading-and-error-substates/#toc_error-substates I'm mostly just proposing using these existing error-handling techniques for catching rendering errors |
In this case, we need to pair |
let's imagine template like this: <div> | ctx1
<div> | ctx2
<ul> | ctx3
{{#each this.items as |item index|}} | ctx3+index
<MyLi @data={{item}} />
{{/each}}
</ul>
</div>
<div> and error in last given: rendering loop started:
|
in ^ this case |
couldn't the error modifier be implicitly implemented for the route template? |
Examples of errors not caught by the existing error handling mechanisms, and what the consequences are: https://codesandbox.io/s/error-examples-ydped |
From a teaching and mental model, thinking about this I had some more thoughts. Since this is a rendering error handler where do we work with rendering: templates. Routes are mostly independent of the rendering cycle. Also because of existing paradigms and different patterns teams may have UI and behavior defined in different locations (Routes, Controllers, Components). In a sense it can be thought that routes are loading/async handlers that populate component args (or currently the state properties on controllers for teams that are controller heavy). Moving render error handling to routes muddles the purpose of routes in my mind because what is a data loader now starts messing more and more with UI/rendering rather than being somewhat @NullVoxPopuli you do make a good point on how to pick the error component for a given route though, this gets a bit trickier. On one hand giving a 1-1 mapping for the existing Route {
async model() {
try {
let value = await somethingAsync();
return { value };
} catch(e) {
return { error: e };
}
}
} Then in the component/template: {{#if @error}}
<SomeErrorHandlingComponent @error={{@error}} />
{{else}}
{{!-- Your usual stuff here --}}
{{/if}} With this approach rendering errors can be handled using modifiers which keeps UI and rendering all a concern of components. |
I get what you're saying. I guess I want some form of error handling by default. In the codesandbox I made, there are situations in which you can break the entire app, or get the outlet repeated over and over. While, I'm all for having the ability to manually handle the error, I think what we have today needs some new defaults. I like what you said about the Route staying away from the rendering layer, and being more something that passes args to the rendering layer. In that case, the route could have a default onRenderError arg that is passed to the rendering context (this rendering context being an implicit layer above the route template), so that we can provide improved DX when accidents happen |
how about "outlet" improvements?
and in ember we can have default-route-error component, with logic to show transition data, route name and other debug stuff |
Considering routes as loading/async handlers for components' arguments seems the right separation of concerns. 👍 Following @lifeart , Just a though, but it would be nice to get additional possibilities to mounting a component {{outlet catchRendering=(component 'defaultErrorComponent')}} with {{yield catchRendering=(action 'handleRenderingError')}} |
Possibly related: emberjs/ember.js#16503 |
This is some very good discussion, but I’m not seeing any action towards an RFC. If someone would like guidance in getting there, let me know and I can reopen. |
Some history first:
componentDidCatch
(from react), is a new~ish feature (as of v2 (16)). It is super handy for catching rendering errors in components. Since react is all components, it'd be silly to have componentDidCatch in every component, so what I ended up doing, was making my ownRoute
component (extending from react-router -- very similar to ember's Route, but less structured, no data handling), where an error catcher component wrapped the sub-tree of components.This provided the following benefits:
componentDidCatch
, so that we may render an appropriate and meaningful error message to not confuse our users and get support requests saying "it's broken, pls send help".So, my question to the community and core team members:
For reference: examples of errors not caught by the existing error handling mechanisms, and what the consequences are: https://codesandbox.io/s/error-examples-ydped
The text was updated successfully, but these errors were encountered: