[Astro] .map() の中でコンポーネントが描画されないときの回避策

Tech
[Astro] .map() の中でコンポーネントが描画されないときの回避策

記事一覧の「3記事目の後」に広告コンポーネントを挟もうとして、.map() の中に条件付きで差し込んだら、そのコンポーネントだけ出力されないという現象に遭遇しました。原因の特定と回避策をメモしておきます。

症状

.map() が返す <Fragment> の中に、PostCard と自作の <AdUnit> を並べたケース。PostCard は出るのに、AdUnit だけ HTML に出てきませんでした。

{posts.map((post, i) => (
  <Fragment>
    <PostCard {...post.data} />
    {i === 2 && <AdUnit slot="..." />}
  </Fragment>
))}

切り分けで分かったこと:

  • <div> など素の要素を同じ位置に置くと出る
  • PostCard(別コンポーネント)は出る
  • 同じ <AdUnit>.map() の外に置くと出る
  • .map() が返す Fragment の中に置いたときだけ AdUnit が消える

つまり「ループ内の Fragment + 特定コンポーネント」の組み合わせで踏む挙動でした。

回避策:配列を分割して、ループの外に置く

.map() の中に差し込むのをやめて、配列を slice で分割し、その間にコンポーネントを置く形にしたら安定しました。

{posts.slice(0, 3).map((post) => <PostCard {...post.data} />)}

{posts.length > 3 && <AdUnit slot="..." />}

{posts.slice(3).map((post) => <PostCard {...post.data} />)}

「N番目の後に1個だけ挟みたい」用途なら、この分割パターンの方がそもそも読みやすく、意図も明確です。複数箇所に挟みたい場合は、reduce で要素配列を組み立ててから描画する手もあります。

教訓

  • ループ内で特定コンポーネントが理由なく消えたら、まず .map() の外で描画してみて切り分ける
  • 「N番目に1個挿入」は、ループ内条件分岐より配列分割の方が堅実

この件は AdSense をブログに入れたときに踏みました。広告まわりの設置の話は Astro に AdSense を入れる記事 にまとめています。

Astro