Next.js×microCMSで構築中のブログに、Twitterの埋め込みが正常に表示されない

実現したいこと

Next.jsとmicroCMSを使ったjamstackブログを構築中。
ブログの記事詳細を表示するためのページとしてNEXTJS/pages/blog/配下に[slug].jsを作成。

microCMSで、記事にTwitterの埋め込みがあるときとないときで挙動が不安定になるため、改善したい。

発生している問題・分からないこと

npx netx devでローカルサーバーでの検証時、以下のような挙動が発生しており、不安定である

前提

  1. microCMSでTwitterの埋め込みのある記事(A・B)と埋め込みのない記事(C)を作成

ローカルサーバー上で見られた挙動

  1. Cに、記事一覧ページ(blog/page/1)などから直接アクセスすると問題なく表示される
  2. Cから、ページネーションを使ってAもしくはBにアクセスすると、記事ページ自体は表示されるがTwitterの埋め込みが表示されない(そのうえで一回ページをリロードすると表示される)
  3. 同様に、AもしくはBに記事一覧ページなどから直接アクセスすると、記事ページ自体は表示されるがTwitterの埋め込みが表示されない(そのうえで一回ページをリロードすると表示される)
  4. AもしくはBで、Twitterの埋め込みが表示されている状態でページネーションを使って前後の記事へ遷移するとエラー発生
  5. AもしくはBのURLをアドレスバーに入れて遷移すると埋め込みが表示される

など、とにかく埋め込みの表示が不安定

エラーメッセージ

error

1【DOMException: Failed to execute 'removeChild' on 'Node': The node to be removed is not a child of this node.】 2 3【The above error occurred in the <blockquote> component. 4React will try to recreate this component tree from scratch using the error boundary you provided, ErrorBoundary.】

該当のソースコード

javascript

1【pages/_document.js】2import { Html, Head, Main, NextScript } from 'next/document'3 4import { siteMeta } from 'lib/constants'5const { siteLang } = siteMeta 6 7export default function Document() {8 return (9 <Html lang={siteLang}>10 <Head>11 <script async src="https://platform.twitter.com/widgets.js"></script>12 </Head>13 <body>14 <Main />15 <NextScript />16 </body>17 </Html>18 )19}20

javascript

1【pages/blog/[slug].js】2import { useEffect, useRef } from 'react'3import { getPostBySlug, getAllSlugs } from 'lib/api'4import { extractText } from 'lib/extract-text'5import { prevNextPost } from 'lib/prev-next-post'6import Meta from 'components/meta'7import Container from 'components/container'8import PostHeader from 'components/post-header'9import PostBody from 'components/post-body'10import {11 TwoColumn,12 TwoColumnMain,13 TwoColumnSidebar,14} from 'components/two-column'15import ConvertBody from 'components/convert-body'16import PostCategories from 'components/post-categories'17import PostTags from 'components/post-tags'18import Share from 'components/sns-sharebtn'19import Pagination from 'components/pagination'20import Image from 'next/image'21import { getPlaiceholder } from 'plaiceholder'22 23// ローカルの代替アイキャッチ画像24import { eyecatchLocal } from 'lib/constants'25 26export default function Post({27 title,28 publish,29 content,30 eyecatch,31 categories,32 tags,33 description,34 prevPost,35 nextPost,36}) {37 useEffect(() => {38 // scriptを読み込み39 const script = document.createElement('script')40 script.src = '//cdn.iframe.ly/embed.js'41 document.body.appendChild(script)42 // アンマウント時に一応scriptタグを消しておく43 return () => {44 document.body.removeChild(script)45 }46 }, [])47 48 return (49 <Container>50 <Meta51 pageTitle={title}52 pageDesc={description}53 pageImg={eyecatch.url}54 pageImgW={eyecatch.width}55 pageImgH={eyecatch.height}56 />57 58 <article>59 <PostHeader title={title} subtitle="Blog Article" publish={publish} />60 61 <figure>62 <Image63 key={eyecatch.url}64 src={eyecatch.url}65 alt=""66 layout="responsive"67 width={eyecatch.width}68 height={eyecatch.height}69 sizes="(min-width: 1152px) 1152px, 100vw"70 priority 71 placeholder="blur"72 blurDataURL={eyecatch.blurDataURL}73 />74 </figure>75 76 <TwoColumn>77 <TwoColumnMain>78 <PostBody>79 <ConvertBody contentHTML={content} />80 </PostBody>81 <div className="sptbOnly share">82 <Share />83 </div>84 </TwoColumnMain>85 <TwoColumnSidebar>86 <div className="sptbOnly pagerWrap">87 <Pagination88 prevText={prevPost.title}89 prevUrl={`/blog/${prevPost.slug}`}90 nextText={nextPost.title}91 nextUrl={`/blog/${nextPost.slug}`}92 />93 </div>94 <PostCategories categories={categories} />95 <PostTags tags={tags} />96 <div className="pcOnly sharebtnWrap">97 <Share />98 </div>99 </TwoColumnSidebar>100 </TwoColumn>101 102 <div className="pcOnly pagerWrap">103 <Pagination104 prevText={prevPost.title}105 prevUrl={`/blog/${prevPost.slug}`}106 nextText={nextPost.title}107 nextUrl={`/blog/${nextPost.slug}`}108 />109 </div>110 </article>111 </Container>112 )113}114 115export async function getStaticPaths() {116 const allSlugs = await getAllSlugs()117 118 return {119 paths: allSlugs.map(({ slug }) => `/blog/${slug}`),120 fallback: false,121 }122}123 124export async function getStaticProps(context) {125 const slug = context.params.slug126 127 const post = await getPostBySlug(slug)128 129 const description = extractText(post.content)130 131 const eyecatch = post.eyecatch ?? eyecatchLocal 132 133 const { base64 } = await getPlaiceholder(eyecatch.url)134 eyecatch.blurDataURL = base64 135 136 const allSlugs = await getAllSlugs()137 const [prevPost, nextPost] = prevNextPost(allSlugs, slug)138 139 return {140 props: {141 title: post.title,142 publish: post.publishDate,143 content: post.content,144 eyecatch: eyecatch,145 categories: post.categories,146 tags: post.tags,147 description: description,148 prevPost: prevPost,149 nextPost: nextPost,150 },151 }152}153

試したこと・調べたこと

上記の詳細・結果

Twitter widgetの読み込みの読み込みのタイミングが悪いのかと考え、もともとcompenets/meta.js内にdocument.js内の<Head> ~ </Head>内に記述を移動した
→検証環境での不安定さに変化なし


補足

特になし

コメントを投稿

0 コメント