\n \n \n
\n \n \n \n )\n}\n\n```\n\n```javascript\n【pages/blog/[slug].js】\nimport { useEffect, useRef } from 'react'\nimport { getPostBySlug, getAllSlugs } from 'lib/api'\nimport { extractText } from 'lib/extract-text'\nimport { prevNextPost } from 'lib/prev-next-post'\nimport Meta from 'components/meta'\nimport Container from 'components/container'\nimport PostHeader from 'components/post-header'\nimport PostBody from 'components/post-body'\nimport {\n TwoColumn,\n TwoColumnMain,\n TwoColumnSidebar,\n} from 'components/two-column'\nimport ConvertBody from 'components/convert-body'\nimport PostCategories from 'components/post-categories'\nimport PostTags from 'components/post-tags'\nimport Share from 'components/sns-sharebtn'\nimport Pagination from 'components/pagination'\nimport Image from 'next/image'\nimport { getPlaiceholder } from 'plaiceholder'\n\n// ローカルの代替アイキャッチ画像\nimport { eyecatchLocal } from 'lib/constants'\n\nexport default function Post({\n title,\n publish,\n content,\n eyecatch,\n categories,\n tags,\n description,\n prevPost,\n nextPost,\n}) {\n useEffect(() => {\n // scriptを読み込み\n const script = document.createElement('script')\n script.src = '//cdn.iframe.ly/embed.js'\n document.body.appendChild(script)\n // アンマウント時に一応scriptタグを消しておく\n return () => {\n document.body.removeChild(script)\n }\n }, [])\n\n return (\n \n \n\n
\n \n\n
\n \n
\n\n \n \n \n \n \n
\n \n
\n
\n \n
\n \n
\n \n \n
\n \n
\n
\n
\n\n
\n \n
\n
\n
\n )\n}\n\nexport async function getStaticPaths() {\n const allSlugs = await getAllSlugs()\n\n return {\n paths: allSlugs.map(({ slug }) => `/blog/${slug}`),\n fallback: false,\n }\n}\n\nexport async function getStaticProps(context) {\n const slug = context.params.slug\n\n const post = await getPostBySlug(slug)\n\n const description = extractText(post.content)\n\n const eyecatch = post.eyecatch ?? eyecatchLocal\n\n const { base64 } = await getPlaiceholder(eyecatch.url)\n eyecatch.blurDataURL = base64\n\n const allSlugs = await getAllSlugs()\n const [prevPost, nextPost] = prevNextPost(allSlugs, slug)\n\n return {\n props: {\n title: post.title,\n publish: post.publishDate,\n content: post.content,\n eyecatch: eyecatch,\n categories: post.categories,\n tags: post.tags,\n description: description,\n prevPost: prevPost,\n nextPost: nextPost,\n },\n }\n}\n\n```\n\n### 試したこと・調べたこと\n- [x] teratailやGoogle等で検索した\n- [x] ソースコードを自分なりに変更した\n- [ ] 知人に聞いた\n- [x] その他\n\n##### 上記の詳細・結果\nTwitter widgetの読み込みのタイミングが悪いのかと考え、もともとcompenets/meta.js内に記述していたをdocument.js内の ~ 内に記述を移動した\n→検証環境での不安定さに変化なし\n\n---\n\n### 補足\n特になし","answerCount":1,"upvoteCount":0,"datePublished":"2024-05-28T03:38:14.434Z","dateModified":"2024-05-29T02:01:06.000Z","acceptedAnswer":{"@type":"Answer","text":"[slug],js内に以下の記述を行っていた\nuseEffect(() => {\n twttr.widgets.load()\n}, [richText])\n\nこれを、components/covert-body.jsに書くことで解決","dateModified":"2024-05-29T02:01:06.000Z","datePublished":"2024-05-28T17:01:06.231Z","upvoteCount":0,"url":"https://teratail.com/questions/2eja4ns827wv2k#reply-hgdr6ztrleteaz"},"suggestedAnswer":[],"breadcrumb":{"@type":"BreadcrumbList","itemListElement":[{"@type":"ListItem","position":1,"name":"トップ","url":"https://teratail.com"},{"@type":"ListItem","position":2,"name":"Next.jsに関する質問","url":"https://teratail.com/tags/Next.js"},{"@type":"ListItem","position":3,"name":"Next.js","url":"https://teratail.com/tags/Next.js"}]}}}
質問するログイン新規登録
Next.js

Next.jsは、Reactを用いたサーバサイドレンダリングなどを行う軽量なフレームワークです。Zeit社が開発しており、nextコマンドでプロジェクトを作成することにより、開発環境整備が整った環境が即時に作成できます。

MicroCMS

MicroCMSとは、APIベースのヘッドレスCMSです。日本製のため、デフォルトで日本語に対応しており、サポートも豊富。管理画面でも作成したコンテンツなどが見やすくシンプルで、APIの管理がしやすい点も特徴です。

JAMstack

JAMstackは、JavaScript・API・Markupの頭文字を取ったWebアプリケーションアーキテクチャです。事前に構築されたHTMLをCDN上で配信。高いパフォーマンスとセキュリティが特徴です。

Twitter

Twitterは、140文字以内の「ツイート」と呼ばれる短文を投稿できるサービスです。Twitter上のほぼ全ての機能に対応するAPIが存在し、その関連サービスが多く公開されています。

JavaScript

JavaScriptは、プログラミング言語のひとつです。ネットスケープコミュニケーションズで開発されました。 開発当初はLiveScriptと呼ばれていましたが、業務提携していたサン・マイクロシステムズが開発したJavaが脚光を浴びていたことから、JavaScriptと改名されました。 動きのあるWebページを作ることを目的に開発されたもので、主要なWebブラウザのほとんどに搭載されています。

Q&A

解決済

1回答

458閲覧

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

alolo

総合スコア9

Next.js

Next.jsは、Reactを用いたサーバサイドレンダリングなどを行う軽量なフレームワークです。Zeit社が開発しており、nextコマンドでプロジェクトを作成することにより、開発環境整備が整った環境が即時に作成できます。

MicroCMS

MicroCMSとは、APIベースのヘッドレスCMSです。日本製のため、デフォルトで日本語に対応しており、サポートも豊富。管理画面でも作成したコンテンツなどが見やすくシンプルで、APIの管理がしやすい点も特徴です。

JAMstack

JAMstackは、JavaScript・API・Markupの頭文字を取ったWebアプリケーションアーキテクチャです。事前に構築されたHTMLをCDN上で配信。高いパフォーマンスとセキュリティが特徴です。

Twitter

Twitterは、140文字以内の「ツイート」と呼ばれる短文を投稿できるサービスです。Twitter上のほぼ全ての機能に対応するAPIが存在し、その関連サービスが多く公開されています。

JavaScript

JavaScriptは、プログラミング言語のひとつです。ネットスケープコミュニケーションズで開発されました。 開発当初はLiveScriptと呼ばれていましたが、業務提携していたサン・マイクロシステムズが開発したJavaが脚光を浴びていたことから、JavaScriptと改名されました。 動きのあるWebページを作ることを目的に開発されたもので、主要なWebブラウザのほとんどに搭載されています。

0グッド

1クリップ

投稿2024/05/28 03:38

編集2024/05/28 08:15

0

1

実現したいこと

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 <Meta 51 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 <Image 63 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 <Pagination 88 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 <Pagination 104 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.slug 126 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

試したこと・調べたこと

  • teratailやGoogle等で検索した
  • ソースコードを自分なりに変更した
  • 知人に聞いた
  • その他
上記の詳細・結果

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


補足

特になし

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答1

0

自己解決

[slug],js内に以下の記述を行っていた
useEffect(() => {
twttr.widgets.load()
}, [richText])

これを、components/covert-body.jsに書くことで解決

投稿2024/05/28 17:01

alolo

総合スコア9

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.29%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問