sveltejs / svelte

Cybernetically enhanced web apps
https://svelte.dev
MIT License
78.21k stars 4.09k forks source link

try block, like error boundary #3733

Open tanhauhau opened 4 years ago

tanhauhau commented 4 years ago

Is your feature request related to a problem? Please describe.

I wonder would it possible to have a {#try} logic block, such that anything goes inside, if error would be handled in the {:catch} block, eg:

{#try}
   <Component />
{:catch}
   <div>Fallback uI </div>
{/try}

if an error thrown during init / update in <Component />, then will see the Fallback UI instead.

Describe the solution you'd like A clear and concise description of what you want to happen.

Describe alternatives you've considered A clear and concise description of any alternative solutions or features you've considered.

How important is this feature to you? Note: the more honest and specific you are here the more we will take you seriously.

Additional context Add any other context or screenshots about the feature request here.

marcus-sa commented 4 years ago

Good idea.

Maybe something like this would benefit even more, depending on the error getting thrown. So basically like a switch statement for catching errors.

<script>
import { GraphQLError } from 'graphql';
</script>

{#try}
   <Component />
{:catch GraphQLError}
    <div></div>
{:catch TypeError}
   <div></div>
{:catch}
   <div>Default fallback ui </div>
{/try}
MintyMods commented 4 years ago

Can see this being useful especially when including components from 3rd parties.

Conduitry commented 4 years ago

This has been brought up before, but I don't think there's a clear way that we could have this work. I don't think it would be too difficult to have handlers for exceptions that synchronously occur as part of an operation on the child component (instantiating it, or updating props for example), but there's not a good way to handle other exceptions that happen during the life of the component. The component would have to emit events (or do something else equivalent to that), but only when it appears inside a {#try}. (In other cases, it should continue to throw exceptions as it currently does.) Every compiled component would have to support being run inside of a {#try}, regardless of whether it ever actually is, because at compile time we can't tell that. This would be a burden on every component whether this feature was being used or not.

tanhauhau commented 4 years ago

Yup, I just realised there's a RFC https://github.com/sveltejs/rfcs/pull/11 that brought up the exact same issue.

Every compiled component would have to support being run inside of a {#try}

That's how context works as well. Every component has to know which context it is currently mounted into.

I would like to propose a similar mechanism, where the error boundary of a component is assignes when mounted, just like context, (or it could be a special context key 🤔) So at the point of error, there's no bubbling up of errors, but rather catching it directly to the preassigned error boundaries

jonatansberg commented 4 years ago

There was some discussion about this in the discord #future channel the other day, an I thought I'd jot down some notes here for posterity.

@halfnelson had created this error boundary proof-of-concept: https://svelte.dev/repl/006facb65ece4f808cd733e838783228?version=3.22.2

If we imagine some sort of event based API, then maybe extending the slot syntax would make more sense?

<script>
  let error;
</script>

{#if error}
  <p>Error: {error}</p>
{:else}
  <slot on:error={({ detail }) => {
    error = detail.message;
    logMyErrorSomewhere(detail);
  }} />
{/if}
jonatansberg commented 3 years ago

I improved a bit on the REPL linked above, adding support for SSR and logging: https://svelte.dev/repl/9d44bbcf30444cd08cca6b85f07f2e2a?version=3.29.4

thedivtagguy commented 2 years ago

Has there been any progress on this? Or an alternate way to handle such errors? I have JSON files containing data for my HTML pages, which contain keys like title, summary, blockquote etc. This file is stored in a variable called post and added into the page as follows:

<main>
    <div class="container max-w-5xl mt-6 px-6">
        <div class="pb-5 mb-5 border-b border-gray-100">
            <h1 class="font-bold text-5xl">{post.title}</h1>
            <h2>{post.summary}</h2>
        </div>
        <article class="prose lg:prose-xl my-4 mx-auto">
        {#each post.text as text}
            <p>{text.value}</p>
        {/each}
        </article>
        <div class="flex flex-wrap">
            {#each post.images as image}
                <div class="w-full ">
                    <img src="{image.value.src}" alt="{image.value.alt}" class="w-full h-auto" />
                </div>
            {/each}
        </div>
    </div>
</main>

Now some posts might have a key while others may not. Currently, Svelte throws an error which stops my build process if any file is missing any key.

It would be good to 'try to look for this key, if it exists then add it otherwise just move on' kind of logic. I could implement an if-else block for each key but is there a more elegant way to handle this?

thedivtagguy commented 2 years ago

Solved my problem by using optional chaining. {post?.title} and so on.

mrcasual commented 2 years ago

@YamiOdymel, this feature request goes beyond simple error handling is more along the lines of React's Error Boundaries.

ghost commented 1 year ago

Hello, everyone. I am currently working on an RFC that can solve this issue.

ghost commented 1 year ago

Here's the RFC: sveltejs/rfcs#69

benbucksch commented 1 year ago

This seems like an RFC for tag extensions, which allows any kind of tags, but doesn't by itself add try/catch. I'd have to "import" that as custom syntax extension. If I understood correctly.

I personally would prefer exception/error handling to be a fundamental part of Svelte.

This concerns a) errors when creating the sub-components b) errors thrown from b.1.) event handlers b.2.) $: reactivity statements, b.3.) {} expressions in the markup b.4.) onMount()/onDestroy() b.5.) and similar entry points, and c) errors thrown up explicitly by JS code within the component.

Right now, a naive app implementation that follows the code patterns in the tutorial will not handle any errors. And handling all error properly requires either a lot of try/catch/emit boiler plate, or a custom error handling infrastructure.

emil14 commented 6 months ago

Any updates due to Svelte5?

sifat-at-work commented 2 months ago

Wouldn't having a hook to deal with errors be better than handling errors in the markup? IMHO it'll make it easy to manipulate the errors.

<script>
import { onError } from "svelte";
import { logger } from "analytics";

let error;

onError((_error) => {
    error = _error;
    logger.error(_error);
})
</script>

{#if error}
   <FallbackUI />
{:else}
   <Component />
{/if}
benbucksch commented 2 months ago

Yes, indeed, I'd like that. This is great. Simple and meers all requirements.

Component level is the right granularity. (If I want to catch something more specific, I can use try/catch.)

This could catch all the other error classes, like exceptions in $: statements, in markup {} code, in the initial execution of the JS section code, and many other error classes.

Errors in sub-components should bubble up and be caught by the onError() of the calling component. This would not force me to add an onError() in every single component. Bubbling would also allow to show errors in a custom UI for a specific bigger component, including errors in generic sub-components. The error UI would depend on the enclosing component, which is what we need for complex user-friendly apps.