[Astro] .map() の中でコンポーネントが描画されないときの回避策
Tech記事一覧の「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