RichText Embed glitches when you scroll

Hi :waving_hand:
Has anyone experienced something similar

Btw it's a custom RichText field
i've also tried using useMemo within my embed component to prevent unnecessary re-renders of the embed itself

The rendered embedded url glitches when you scroll on the site

const reconstructEmbedUrl = (originalUrlString) => {
  if (!originalUrlString) {
    return originalUrlString;
  }

  try {
    const tempUrl = new URL(originalUrlString);
    const hostname = tempUrl.hostname.toLowerCase();
    const pathname = tempUrl.pathname;

    if (hostname.includes('youtube.com') || hostname.includes('youtu.be')) {
      let videoId = null;
      if (hostname.includes('youtu.be')) {
        videoId = pathname.substring(1);
      } else if (pathname.includes('/watch')) {
        videoId = tempUrl.searchParams.get('v');
      } else if (pathname.includes('/embed/')) {
        videoId = pathname.split('/embed/')[1]?.split('/')[0];
      }

      if (videoId) {
        const embedUrl = new URL(`https://www.youtube.com/embed/${videoId}`);
        embedUrl.searchParams.set('rel', '0');
        if (tempUrl.searchParams.has('t')) {
          embedUrl.searchParams.set(
            'start',
            tempUrl.searchParams.get('t').replace('s', ''),
          );
        }
        return embedUrl.toString();
      }
    }

    if (hostname.includes('vimeo.com')) {
      const pathParts = pathname.split('/');
      const videoId = pathParts.pop() || pathParts.pop();

      if (videoId && /^\d+$/.test(videoId)) {
        if (
          hostname.includes('player.vimeo.com') &&
          pathname.includes('/video/')
        ) {
          return originalUrlString;
        }
        return `https://player.vimeo.com/video/${videoId}`;
      }
    }
  } catch (e) {
    return originalUrlString;
  }

  return originalUrlString;
};
 embed: ({node}) => {
    const {url, customHeight, isDatawrapper, oembedHtml} = React.useMemo(() => {
      let processedUrl = null;
      let height = null;
      let datawrapper = false;
      let htmlContent = node.oembed?.html || null;

      try {
        const initialEmbedUrlString = node.oembed?.embed_url;
        if (
          !initialEmbedUrlString ||
          typeof initialEmbedUrlString !== 'string' ||
          initialEmbedUrlString.trim() === ''
        ) {
          return {
            url: processedUrl,
            customHeight: height,
            isDatawrapper: datawrapper,
            oembedHtml: htmlContent,
          };
        }

        const reconstructedEmbedUrlString = reconstructEmbedUrl(
          initialEmbedUrlString,
        );

        processedUrl = new URL(reconstructedEmbedUrlString);
        height = processedUrl.searchParams.get('height');

        if (height) {
          processedUrl.searchParams.delete('height');
        }

        if (node.oembed) {
          datawrapper =
            (node.oembed.provider_name &&
              node.oembed.provider_name
                .toLowerCase()
                .includes('datawrapper')) ||
            (node.oembed.provider_url &&
              node.oembed.provider_url.includes('datawrapper')) ||
            (initialEmbedUrlString &&
              (initialEmbedUrlString.includes('datawrapper.dwcdn.net') ||
                initialEmbedUrlString.includes('datawrapper.de')));
        }
      } catch (error) {
        console.error(
          'Error processing embed URL:',
          node.oembed?.embed_url,
          error,
        );
        return {
          url: null,
          customHeight: null,
          isDatawrapper: false,
          oembedHtml: htmlContent,
        };
      }
      return {
        url: processedUrl,
        customHeight: height,
        isDatawrapper: datawrapper,
        oembedHtml: htmlContent,
      };
    }, [
      node.oembed?.embed_url,
      node.oembed?.html,
      node.oembed?.provider_name,
      node.oembed?.provider_url,
    ]);

    if (!node.oembed) {
      console.error('Missing oembed data for node, returning null:', node);
      return null;
    }

    if (oembedHtml) {
      let divStyleHeight = undefined;
      if (!isDatawrapper) {
        if (node.oembed.height) {
          divStyleHeight = `${node.oembed.height}px`;
        } else if (customHeight) {
          divStyleHeight = `${customHeight}px`;
        } else {
          divStyleHeight = '400px';
        }
      }

      return (
        <div
          className={`${styles.iframe} ${!isDatawrapper ? styles.default : ''}`}
          style={{
            height: divStyleHeight,
            position: 'relative',
            width: '100%',
          }}
          dangerouslySetInnerHTML={{__html: oembedHtml}}
        />
      );
    }

    if (!url) {
      console.log('Embed URL is not available, cannot render iframe.');
      return null;
    }

    return (
      <div
        className={styles.iframe}
        style={{
          height: customHeight ? `${customHeight}px` : undefined,
          position: 'relative',
          width: '100%',
        }}
      >
        <iframe
          style={{
            position: !customHeight ? 'absolute' : undefined,
            top: !customHeight ? 0 : undefined,
            left: !customHeight ? 0 : undefined,
            width: '100% !important',
            height: customHeight
              ? `${customHeight}px`
              : !customHeight
                ? '100%'
                : 230,
          }}
          src={url.toString()}
          title={node.oembed.title || 'Embedded content'}
          data-oembed={node.oembed.embed_url}
          data-oembed-type={node.oembed.type}
          data-oembed-provider={node.oembed.provider_name}
          frameBorder='0'
          allowFullScreen
          loading='lazy'
        />
      </div>
    );
  },
1 Like

Might be a re-render on scroll maybe from how the RichText is handled?

try adding console.log inside a useEffect in your component, if you see that log message every time you scroll past it, it's re-rendering it

1 Like

Hey man, forgot to feedback
I actually just wrapped the custom RichTextField in a useMemo

1 Like