solidjs / solid-meta

Write meta tags to the document head
127 stars 16 forks source link

Error in solid-meta when navigating solid-start app: The node to be removed is not a child of this node #38

Closed knpwrs closed 5 months ago

knpwrs commented 9 months ago

https://lets.church is a solid-start app (source code here: https://github.com/LetsChurch/lets.church). When I upgraded from @solidjs/meta@0.28.6 to @solidjs/meta@0.29.1 I started seeing a bug where when I would navigate to a particular route (/media/[id]) and then navigate to another instance of that route, the app would crash:

Screen Recording 2023-11-14 at 9 19 27 PM

This is the line that is crashing: https://github.com/solidjs/solid-meta/blob/283df1b99dd085cb61981f0a66335b75350f59a5/src/index.tsx#L85

And this is the error: DOMException: Node.removeChild: The node to be removed is not a child of this node.

This is the <Title> element in my media/[id].tsx route file, as well as my <Og /> component which renders a number of <Meta /> tags:

https://github.com/LetsChurch/lets.church/blob/adf8502d3fa73519b7279c4336b2910cc44eedb7/apps/web/src/routes/(root)/media/%5Bid%5D.tsx#L465-L477

I have no real idea what's going on here, but I do know that this behavior is happening with:

This is not happening with:

This also only reproduces in production builds and not in development mode.

This commit acts as a temporary fix by downgrading @solidjs/meta, solid-js, and solid-start (this is the currently deployed commit): https://github.com/LetsChurch/lets.church/commit/af443f3a8d9e65e6a9445bddb1babded030cddea

knpwrs commented 9 months ago

I just tried removing the <Og /> component from media/[id].tsx and the app no longer crashes, even with the upgrade.

commit 38c8fef8d168b57631ddc8eac33487388247073b
Author: Ken Powers <ken@lets.church>
Date:   Tue Nov 14 21:30:22 2023 -0500

    DEBUG

diff --git a/apps/web/src/routes/(root)/media/[id].tsx b/apps/web/src/routes/(root)/media/[id].tsx
index e1a190e..027e2ee 100644
--- a/apps/web/src/routes/(root)/media/[id].tsx
+++ b/apps/web/src/routes/(root)/media/[id].tsx
@@ -48,7 +48,6 @@ import Pagination from '~/components/pagination';
 import { Avatar } from '~/components/avatar';
 import FloatingDownloadMenu from '~/components/floating-download-menu';
 import { useUser } from '~/util/user-context';
-import Og from '~/components/og';
 import UnderbarButton from '~/components/media/underbar-button';
 import RatingButtons from '~/components/media/rating';

@@ -463,18 +462,6 @@ export default function MediaRoute() {
   return (
     <>
       <Title>{metaData()?.data.title ?? '...'} | Let's Church</Title>
-      <Og
-        title={
-          metaData()?.data.title
-            ? `${metaData()?.data.title} | Let's Church`
-            : "Let's Church"
-        }
-        description={metaData()?.data.description ?? ''}
-        image={
-          metaData()?.data.thumbnailUrl ??
-          metaData()?.data.channel.defaultThumbnailUrl
-        }
-      />
       <div class="md:grid md:grid-cols-3 md:gap-4">
         <div class="space-y-4 md:col-span-2">
           <Player

Here is the source code of that <Og /> component for reference:

import { Meta, useLocation } from 'solid-start';
import bannerUrl from './og-banner.png';
import type { Optional } from '~/util';

export type Props = {
  title: string;
  description: string;
  image?: Optional<string>;
};

export default function Og(props: Props) {
  const loc = useLocation();
  const url = () => `https://lets.church/${loc.pathname}${loc.search}`;

  return (
    <>
      <Meta name="description" content={props.description} />

      <Meta property="og:url" content={url()} />
      <Meta property="og:type" content="website" />
      <Meta property="og:title" content={props.title} />
      <Meta property="og:description" content={props.description} />
      <Meta property="og:image" content={props.image ?? bannerUrl} />

      <Meta name="twitter:card" content="summary_large_image" />
      <Meta property="twitter:domain" content="lets.church" />
      <Meta property="twitter:url" content={url()} />
      <Meta name="twitter:title" content={props.title} />
      <Meta name="twitter:description" content={props.description} />
      <Meta name="twitter:image" content={props.image ?? bannerUrl} />
    </>
  );
}
indeyets commented 6 months ago

I had similar issue in relation to another removeNode call in solid-meta. It seems, like a working fix is to always check that element has a parent, before trying to remove it from DOM.

so, instead of this:

if (lastVisited && lastVisited.ref) {
  document.head!.removeChild(lastVisited.ref);
}

This should be used:

if (lastVisited && lastVisited.ref && lastVisited.ref.parentNode) {
  document.head!.removeChild(lastVisited.ref);
}

It is not a proper fix from the first principles, as it doesn't take care of the reason nodes were orphaned