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

Error in EnhancedForm_destroy - Uncaught (in promise) TypeError: EnhancedForm.remove is not a function #500

Open
1 task done
oscarhermoso opened this issue Nov 2, 2024 · 3 comments
Labels
bug Something isn't working question Further information is requested

Comments

@oscarhermoso
Copy link

oscarhermoso commented Nov 2, 2024

  • Before posting an issue, read the FAQ and search the previous issues.

Description

I have a pretty dense SPA superform component that throws an error from EnhancedForm_destroy, because of Uncaught (in promise) TypeError: EnhancedForm.remove is not a function.

It seems that EnhancedForm is expected to be a HTMLElement with a remove() method. This is fine on Chrome.

However, on Firefox, there is no remove() method - I believe it is a DOM Node.

EDIT: I have reproduced on both Chrome and Firefox.

2024-11-02.16-57-54.mp4

The error is bubbling from here:

function EnhancedForm_destroy() {
if (EnhancedForm?.parentElement) {
EnhancedForm.remove();
}
EnhancedForm = undefined;
}

onDestroy(() => {
Unsubscriptions_unsubscribe();
NextChange_clear();
EnhancedForm_destroy();
for (const events of Object.values(formEvents)) {
events.length = 0;
}
formIds.get(_currentPage)?.delete(_initialFormId);
});

If applicable, a MRE

Apologies, I have attempted to reproduce in a minimal REPL, but I have been unable to.

Discovered error on sveltekit-superforms v2.12.3, attempted to upgrade to sveltekit-superforms, v2.19.1 and issue is still occurring.

Firefox version 126.0 (64-bit)
Chrome Version 128.0.6613.137

@oscarhermoso oscarhermoso added the bug Something isn't working label Nov 2, 2024
@oscarhermoso oscarhermoso changed the title Uncaught (in promise) TypeError: EnhancedForm.remove is not a function on Firefox Error in EnhancedForm_destroy - Uncaught (in promise) TypeError: EnhancedForm.remove is not a function Nov 2, 2024
@oscarhermoso
Copy link
Author

oscarhermoso commented Nov 3, 2024

Ok - I have figured out the root cause - but I believe that a fix still needs to be made to the sveltekit-superforms library.

Given below form, when this form was unmounted from the page, SuperForm attempted to call .destroy() on an element. However, something about having an input with attribute name="remove" caused it to break.

Renaming the input to have attribute name="removed" solved the problem (note the extra "d").

It would be good to have a fix - ideally, I don't think that the name of HTML elements should affect the inner workings of SuperForm.

See below the full code that was causing issues. Apologies, but it is still difficult for me to create an MRE.

Code

<script lang="ts" context="module">
	import { z } from 'zod';

	export const fileUsersSchema = z
		.object({
			publicId: z.string(),
			users: z
				.object({
					publicId: z.string(),
					remove: z.boolean().optional(),
				})
				.array(),
		})
		.strip();

	let uniqueId = 0;
</script>

<script lang="ts">
	import { page } from '$app/stores';
	import { defaults, superForm } from 'sveltekit-superforms';
	import { zod } from 'sveltekit-superforms/adapters';
	import type { RoomFile } from '$routes/room/+server';
	import { createEventDispatcher } from 'svelte';

	export let file: RoomFile;

	const id = uniqueId++;
	let serverError = '';

	const dispatch = createEventDispatcher<{
		'file-users-updated': void;
	}>();

	const { form, enhance } = superForm(defaults(file, zod(fileUsersSchema)), {
		dataType: 'json',
		validators: zod(fileUsersSchema),
		onSubmit: ({ jsonData }) => {
			jsonData({ publicId: $form.publicId, users: $form.users.filter((u) => u.remove) });
		},
		onError({ result }) {
			serverError = result.error.message;
		},
		onUpdated: ({ form }) => {
			if (form.valid && file) {
				file.users = file.users.filter((user) =>
					form.data.users.some((u) => user.publicId === u.publicId),
				);
				dispatch('file-users-updated');
			}
		},
	});
</script>

<form method="POST" use:enhance action="/files/{file.publicId}?/modify-file-users">
	<input type="hidden" name="publicId" value={$form.publicId} />

	<div class="my-3"><b>People with access</b></div>
	<ul class="list-group my-3 overflow-auto mh-25">
		<li class="list-group-item d-flex justify-content-between">
			<small class="d-flex align-items-center"
				>{$page.data.session.user.firstName} {$page.data.session.user.lastName} (You)</small
			>
			<button type="button" class="btn btn-light btn-sm" disabled>
				{file.ownerId === $page.data.session.user.publicId ? 'Owner' : ''}
			</button>
		</li>
		{#each file.users as user, i (user.publicId)}
			<li
				class="list-group-item d-flex justify-content-between"
				class:d-none={$form.users[i].remove}
			>
				<small class="d-flex align-items-center">{user.firstName} {user.lastName}</small>
				{#if file.access.sharing}
					<input type="hidden" name="publicId" value={user.publicId} />
					<input
						type="checkbox"
						class="btn-check"
						id="file-user-{id}-{i}"
						autocomplete="off"
						bind:checked={$form.users[i].remove}
						name="remove"
					/> <!-- ^^^^^^^^^^^^^ this was the problem -->
					<label class="btn btn-light btn-sm" for="file-user-{id}-{i}">
						<i class="bi bi-x-lg" />
					</label>
				{/if}
			</li>
		{/each}
	</ul>
	{#if serverError}
		<div class="invalid-feedback d-block">{serverError}</div>
	{/if}
</form>

@ciscoheat
Copy link
Owner

Thank you for the report, but this is very strange, how can the attribute of an input element affect the parent DOM object of the element's form? I have never seen that before.

@ciscoheat ciscoheat added the question Further information is requested label Nov 4, 2024
@oscarhermoso
Copy link
Author

oscarhermoso commented Nov 7, 2024

I am not sure why it is occurring, but I have found some time to create an MRE.

Steps to reproduce

  1. Open browser console
  2. Enter a username
  3. Click submit

Expected

User can be added/removed with the form (expected behaviour can be reproduced by commenting out <Form /> and uncommenting <FixedForm /> in +page.svelte)

Actual

If input has attribute name="remove", errors with:

Uncaught TypeError: node.remove is not a function
    at destroy_effect (chunk-7G4UXERQ.js:1371:12)
    at chunk-7G4UXERQ.js:1411:5
    at run_out_transitions (chunk-7G4UXERQ.js:1423:5)
    at pause_effect (chunk-7G4UXERQ.js:1410:3)
    at chunk-ORL7STRW.js:567:9
    at update_reaction (chunk-7G4UXERQ.js:1714:23)
    at update_effect (chunk-7G4UXERQ.js:1805:21)
    at process_effects (chunk-7G4UXERQ.js:1932:11)
    at flush_queued_root_effects (chunk-7G4UXERQ.js:1863:7)
    at process_deferred (chunk-7G4UXERQ.js:1894:3)

else if input has attribute bind:checked={$form.users[i].remove}, errors with:

chunk-7G4UXERQ.js?v=a0a9029d:1650 Uncaught TypeError: EnhancedForm.remove is not a function
    at EnhancedForm_destroy (sveltekit-superforms.js?v=a0a9029d:2053:20)
    at sveltekit-superforms.js?v=a0a9029d:1502:7
    at untrack (chunk-7G4UXERQ.js?v=a0a9029d:2098:12)
    at chunk-ORL7STRW.js?v=a0a9029d:2704:23
    at chunk-ORL7STRW.js?v=a0a9029d:2626:11
    at execute_effect_teardown (chunk-7G4UXERQ.js?v=a0a9029d:1326:17)
    at destroy_effect (chunk-7G4UXERQ.js?v=a0a9029d:1386:3)
    at destroy_effect_children (chunk-7G4UXERQ.js?v=a0a9029d:1347:5)
    at destroy_effect (chunk-7G4UXERQ.js?v=a0a9029d:1376:3)
    at destroy_effect_children (chunk-7G4UXERQ.js?v=a0a9029d:1347:5)

Note that it is quite inconsistent. I have found that I reproduce the error more consistently after spending more time in the error/making changes - so it may be related to local dev/HMR?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working question Further information is requested
Projects
None yet
Development

No branches or pull requests

2 participants