Skip to content
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

Nested async components error when switching components #3448

Open
taylorzane opened this issue Aug 22, 2019 · 22 comments
Open

Nested async components error when switching components #3448

taylorzane opened this issue Aug 22, 2019 · 22 comments
Labels
compiler Changes relating to the compiler

Comments

@taylorzane
Copy link
Contributor

taylorzane commented Aug 22, 2019

Describe the bug

When nesting dynamic components that are imported via await import('/SomeComponent.js'), when the top-most component class is changed, the following error is thrown.

Logs

TypeError: undefined is not an object (evaluating 'outros.c')

(full traceback below)

To Reproduce

I was unable to reproduce this in the REPL (I assume it doesn't support dynamic imports), so here is a repro repository: https://github.com/taylorzane/svelte-async-import-repro

Just run yarn build && yarn start and then open up the webpage and attempt to click the button. It will switch the to-be-imported component from Child1 to Child2 (both of which dynamically import SubChild)

Expected behavior

The component switch/transition should not throw an error.

Stacktraces

Stack trace
[Error] Unhandled Promise Rejection: TypeError: undefined is not an object (evaluating 'outros.c')
	transition_out (Child1.js:150)
	o (Child1.js:603)
	transition_out (bundle.js:165)
	p (bundle.js:358)
	p (bundle.js:455)
	update (bundle.js:126)
	flush (bundle.js:100)
	promiseReactionJob

Information about your Svelte project:

  • Your browser and the version: Safari 12.1.2

  • Your operating system: macOS 10.14.6

  • Svelte version: v3.9.1

  • Rollup

Severity

Currently makes multi-page (sapper) svelte usage entirely unusable.

Additional context

Might be related to #3165, but seems slightly different (outros is definitely defined)

@taylorzane taylorzane changed the title Nested async components errors when switching components Nested async components error when switching components Aug 22, 2019
@telemmaite
Copy link

I have the same issue using sveltestrap (bootstrap4) components which are imported. In this case areas is array which is dynamically updated via fetch, on user input.

<Dropdown inNavbar=true {isOpen} toggle={() => (isOpen = !isOpen)}>
    <DropdownMenu >
        {#each areas as area}
            <DropdownItem>{area.az_name}</DropdownItem>
        {/each}
    </DropdownMenu>
</Dropdown>

The first time it loads fine and the dropdown list is loaded properly any next update fails.
Stack trace

index.mjs:649 Uncaught (in promise) TypeError: Cannot read property 'c' of undefined
    at is (index.mjs:649)
    at Object.o (DropdownItem.svelte:52)
    at transition_out (index.mjs:684)
    at Object.o (App.svelte:60)
    at transition_out (index.mjs:684)
    at out (App.svelte:59)
    at Object.p (App.svelte:59)
    at Object.p (DropdownMenu.svelte:26)
    at ls (index.mjs:604)
    at es (index.mjs:578)

Debugging with source map it leads to here:

function transition_out(block, local, detach, callback) {
    if (block && block.o) {
        if (outroing.has(block))
            return;
        outroing.add(block);
//! outros is undefined below 
        outros.c.push(() => {
            outroing.delete(block);
            if (callback) {
                if (detach)
                    block.d(1);
                callback();
            }
        });
        block.o(local);
    }
}

@taylorzane
Copy link
Contributor Author

Simply adding guards before the outros.c.push (and a couple other places related to transitioning out) seems to fix the issue from an error standpoint, but I don't know what implications that has with regards to performance/GC.

@kroshilin
Copy link

kroshilin commented Sep 19, 2019

Found similar (same?) bug while using https://github.com/c0bra/svelma
Debugging leads to the same place as @telemmaite pasted.
Here is link to repo with reproduced issue - https://github.com/kroshilin/svelma-transition-bug.
Noticeable, that if I copy-paste bugged components code to my src, and use them instead of importing from library, bug disappears. Method, suggested by @taylorzane works.

@tcrowe
Copy link
Contributor

tcrowe commented Sep 23, 2019

I'm not using anything async and I'm getting this error.

I wrote this patch:

diff --git a/src/runtime/internal/transitions.ts b/src/runtime/internal/transitions.ts
index ed23d3c1..7098afbd 100644
--- a/src/runtime/internal/transitions.ts
+++ b/src/runtime/internal/transitions.ts
@@ -53,13 +53,15 @@ export function transition_out(block, local: 0 | 1, detach: 0 | 1, callback) {
 		if (outroing.has(block)) return;
 		outroing.add(block);
 
-		outros.c.push(() => {
-			outroing.delete(block);
-			if (callback) {
-				if (detach) block.d(1);
-				callback();
-			}
-		});
+		if (outros !== undefined) {
+			outros.c.push(() => {
+				outroing.delete(block);
+				if (callback) {
+					if (detach) block.d(1);
+					callback();
+				}
+			});
+		}
 
 		block.o(local);
 	}

Before accessing outros.c we first check if outros exists. By default it is undefined.

Alternative solutions:

  • call group_outros before transition_out
  • call check_outros before transition_out

Both create the outros object.

If someone in the know wants to chime in with the appropriate solution. I will create the PR.

@ghost
Copy link

ghost commented Sep 25, 2019

I'm having the same problem as well, but using the patch from @tcrowe fixes everything.

Until a fix is accepted for this I'm patching node_modules myself and using the patch-package package to apply the changes after npm install, but I was just wondering if someone more knowledgeable about the codebase can say if there are any downsides to this fix, or if there's a better alternative?

@hmmhmmhm
Copy link

hmmhmmhm commented Oct 3, 2019

It's really critical... I can't use the components of svemla as overlays because of this error.

@dasDaniel
Copy link

dasDaniel commented Oct 5, 2019

same/similar issue

can be seen on link:
https://github.com/dasDaniel/svelte-table/blob/master/example/App.svelte
when using select>option to toggle child component

child component:
https://github.com/dasDaniel/svelte-table/blob/master/src/SvelteTable.svelte

the onDestroy doesn't get called. just get the

Uncaught (in promise) TypeError: Cannot read property 'c' of undefined at transition_out$1

pointing to my each loop {#each c_rows as row, n}

there are no transitions

@hmmhmmhm
Copy link

does anyone have a solution to this?

@ghost
Copy link

ghost commented Oct 13, 2019

If I understand what everyone here means by "dynamic components" as "components built from a separate Svelte project, of which the final JS file is dynamically loaded" then the reason for these errors is because Svelte bundles with its own copy of the runtime internals (or rather, by default that's what bundlers do with Svelte).

I'm making this assumption because otherwise you would just download the .svelte files and include them in your project like usual, in which case they would get bundled with the same internals as the rest of your project anyway, and the OP gave the example of import()ing a .js file.

Unfortunately if this is the case then any suggested fixes relating to guards etc. only half-fix the problem because there are still incorrect state tracking variables lying around which could cause more problems later on, even if no errors are thrown.

The runtime internals use module level variables to track state, so when a component from one project calls a component from another project and both use their own copies of the internals things start going wrong due to the state tracking variables not being shared.

I started an issue specifically about this here: #3671

In the end @halfnelson came up with a solution where the pre-built component should declare at least the svelte/internal imports as globals (rollup) or externals (webpack).

You can see that comment and my comment afterwards stating what I did here: #3671 (comment)

If this is the problem that's being discussed here then it would seem that projects distributing pre-built Svelte components should set their bundles to be built expecting at least the svelte/internal import to be provided as a global somewhere.

In the case where they want both the option to be imported into an existing Svelte project or be used as a standalone component in a non-svelte project (in this case they would need their own internals) then they could set up the build to use globals if they exist or their own copy if they don't exist. I haven't looked into how to go about doing that myself though.

If that's not possible then a project distributing pre-built Svelte components could provide two builds quite easily; one that contains a copy of the internals and one that expects them to be available somewhere in the environment, like as a global. The one that expects the internals to be available somewhere in the environment is the one that would be used inside a Svelte component built as a separate project.

The only downside to this is that if the components were built using different versions of Svelte then it's possible that the expected functionality of each component will go out of sync due to API changes. This is one of the trade offs you have to make when you decide to include a pre-built component into your project anyway though.

@dasDaniel
Copy link

Thanks for the thorough reply.

To clarify, in my comment, by dynamic component I meant that I'm using a condition to determine whether to render/mount component.

Also I've tried using a local (uncompiled) and a svelte file from npm. The error happens in both cases. I believe, if I understand the comment and the referenced issue correctly, that should only be the case for the compiled component. So I don't think this solution would work for me.

@ghost
Copy link

ghost commented Oct 13, 2019

@dasDaniel I just downloaded your SvelteTable project and had a go with the example folder.

Your example project is also bundling two different copies of the Svelte internals, so it's the same problem, but the source of the problem is different. That's why the error states that outros$1 is undefined; because outros as a variable name has already been taken by the first copy of the internals.

I don't know Rollup so I can't help you out with why it's happening, but if you copy the SvelteTable.svelte file into the example folder and then import from ./SvelteTable.svelte it seems to work just fine because it only bundled one internals copy and both components use it.

An easy test to see if two components are using the same internals or not is to use the context API. In the parent component set any random key value pair. If the parent and child are using the same internals then you should be able to retrieve your value in the child using getContext. If they're using different copies of the internals then the child's getContext call will always return undefined because the context API relies on calling the same svelte/internal functions that close over module level state tracking variables.

@dasDaniel
Copy link

thanks @jakelucas

OK, so when I was testing it, I was toggling the way the component is included (relative vs package) I got the same error with both.

I got the same error because I used npm start, which doesn't rebuild the bundle.js file.

🤦‍♂×💯

Now after using npm install && npm run dev the error has gone away, no matter which way I include the child component, and I can no longer reproduce. 🤷‍♂

@arggh
Copy link
Contributor

arggh commented Nov 8, 2019

I'm running Svelte in a Meteor app, which imports a single component from a local npm package that contains a bunch of Svelte components.

Whenever that component is used on a route, and I navigate away from that route, I get the same error:

Stack
Uncaught (in promise) TypeError: Cannot read property 'c' of undefined
    at transition_out (index.mjs:649)
    at Object.outro [as o] (TextField.svelte:26)
    at transition_out (index.mjs:657)
    at Object.outro [as o] (PageProfile.svelte:39)
    at transition_out (index.mjs:657)
    at Object.outro [as o] (PageSection.svelte:5)
    at transition_out (index.mjs:657)
    at Object.outro [as o] (PageProfile.svelte:32)
    at transition_out (index.mjs:657)
    at Object.outro [as o] (MainLayout.svelte:22)

The component in question uses another component internally from the same package. If I remove the sub-component, the error goes away:

<script>
   // mypackage/src/TextField/TextField.svelte

   import Field from '../Field/Field.svelte'; // <-- Remove this line and errors go away
   export let label;
   export let value;
</script>

<Field {label}> <!-- Remove this line and errors go away -->
  <input bind:value/>
</Field> <!-- Remove this line and errors go away -->

@arggh
Copy link
Contributor

arggh commented Nov 8, 2019

This happens also with 3.13.0-alpha.2

@beinoriusju
Copy link

This is definitely a bug. Here is how I ended up having this problem.

  1. I have compiled Car application that via props takes Engine component and renders this component using <svelte:component this={engine} /> syntax.
  2. My friend makes Engine component bundle and I include this bundle via browser script tags and pass into Car application as prop.
  3. It works if Engine does not include any other svelte components, but throws errors described by others if Engine has at least one other component included.

I use svelte 3.0.0 and bundle with rollup.

//My friends Engine.svelte that causes problems 👎
<script>
	import EnginePart1 from './EnginePart1.svelte';
	import EnginePart2 from './EnginePart2.svelte';
	import EnginePart3 from './EnginePart3.svelte';
</script>

<EnginePart1 />
//My friends Engine.svelte that works
<script>
	import EnginePart1 from './EnginePart1.svelte';
	import EnginePart2 from './EnginePart2.svelte';
	import EnginePart3 from './EnginePart3.svelte';
</script>

I'm engine without parts. 👍

And here is an Engine app. Notice condition is part of an error scenario.

<script>
    export let engine;

    let status = 'engine';

    const toggleInfo = () => {
        if (status == 'engine') {
            status = 'something else';
        } else {
            status = 'engine';
        }
    }
</script>

<button on:click|preventDefault={toggleInfo}>Toggle engine</button>

{#if status == 'engine'}        
    <svelte:component this={engine}></svelte:component>
{:else}
    something else
{/if}

Here is how they meet:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Car App</title>
    <link href="bundle.css" rel="stylesheet" />
</head>
<body>
    <div id="parking-lot"></div>
 
    <script src="bundle.js"></script>
    <script src="Engine/bundle.js"></script>
    <script>
        new Car({
            target: document.querySelector('#parking-lot'),
            props: {
                engine: Engine
            }
        })
    </script>
</body>
</html>

This time I get an error TypeError: (void 0) is undefined

But same structural configuration caused TypeError: Cannot read property 'c' of undefined error in other app I'm building.

@Egnus
Copy link

Egnus commented May 16, 2020

I can also confirm that this issue happens, in my case is by using svelte-routing with if conditions.
with Svelte 3.20.1 everything works but it fails with the changes added in Svelte 3.21.0.

Probably fixes related to outros, so one of these 5: #3202, #3410, #3685, #4620, #4630

@retog
Copy link

retog commented May 21, 2020

@tcrowe could you create a PR with your patch? It's so frustrating to be blocked by this issue even if the solutions seems to be there.

(downgrading to 3.20.1 didn't work for me)

@dasDaniel
Copy link

dasDaniel commented Jun 3, 2020

seeing the issue using a compiled component at https://svelte.dev/repl/3be030068a4646fc9c0ce71296cc9c48?version=3.23.0

added repo with error: https://github.com/dasDaniel/svelte-listbox

@Egnus
Copy link

Egnus commented Jun 3, 2020

Thanks for the example @dasDaniel, hope it gives a better idea of what happens.

@retog delete the entire node__modules, force fixed version 3.20.1 and get rid of any .lock file before install all again.
Then check that your Svelte in node_modules is that version in its package.json

I wish I could know which is the specific trigger that breaks this because there are other fixes that I want to get from the updates of Svelte and I can't.

@retog
Copy link

retog commented Jun 20, 2020

@Egnus I did everything as you recommended, there's no version other than [email protected] in node_modules, still ending up with TypeError: outros is undefined on switsching view.

@sudomf
Copy link

sudomf commented Sep 17, 2020

Hey guys, any update on that? I'm experiencing the same problem with simple components in an if-else block.

@tanhauhau
Copy link
Member

@maykefreitas can help provide a repro for more context of what's happening for your case?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
compiler Changes relating to the compiler
Projects
None yet
Development

No branches or pull requests