🍎
Reactで配列データが空のときに非表示のベストプラクティスを探している
要件1
次のような感じで空かもしれない配列を受け取ってリストを表示するコンポーネントがあるとします。
0件のときは要素自体をレンダリングしたくないします。
またレイアウトに関する責務を基本的は負いたくないとします。
components/ItemList.tsx
export default ({ items }: { items: string[] }) => {
if (items.length === 0) return null
return (<ul>
{ items.map((item) => (
<li key={item}>
{item}
</li>
)
}
</ul>)
}
要件2
ItemList
を利用する側のコンポーネントはレイアウトを行い、用意したデータをItemList
コンポーネントに渡します。
index.module.css
.main {
display: flex;
flex-direction: columns;
gap: 64px;
}
.section {
width: 100%;
max-width: 960px;
padding-left: 56px;
padding-right: 56px;
}
pages/index.tsx
import ItemList from 'components/ItemList'
import indexStyles from 'index.module.css'
export default () => {
const items = ['要素1', '要素2', '要素3', '要素4']
return (
<main className={indexStyles.main}>
<header className={indexStyles.section}>
<h1>見出し</h1>
</header>
<section className={indexStyles.section}>
カバーエリア
</section>
<section className={indexStyles.section}>
<ItemList items={items} />
</section>
<footer className={indexStyles.section}>
© Hoge
</footer>
</main>
)
}
課題
このとき、 items = []
の場合のレンダリング結果は次のようになります。
空のsection
が出来るため不自然な余白が発生します。これを解決したい。
<main class="main">
<header class="section">
<h1>見出し</h1>
</header>
<section class="section">
カバーエリア
</section class="section">
<section class="section"></section>
<footer class="section">
© Hoge
</footer>
</main>
解決案1
section
の有無は利用側の責務なので利用側でも分岐を入れてレンダリングをコントロールする案。
でもこれだとちょっと冗長だなーと思っています。
pages/index.tsx(部分)
{(items.length > 0 && (
<section className={indexStyles.section}>
<ItemList items={items} />
</section>
)}
解決案2
ItemList
にレイアウトのロジック自体は持たせないものの、レイアウト情報を横流しできるようにする案。
これはこれで単純なものはいいのですが、ItemList内部でルート要素に表示ロジックとの干渉を意識する必要があったり心理的なコストが高い印象です。
components/ItemList.tsx
export default ({ items, className }: { items: string[], className?: string }) => {
if (items.length === 0) return null
return (<ul className={className}>
{ items.map((item) => (
<li key={item}>
{item}
</li>
)
}
</ul>)
}
pages/index.tsx(部分)
<ItemList items={items} className={indexStyles.section} />
暫定まとめ
.section
に何をさせるかという設計によるとは思いつつ、基本的にはセマンティクスを意識すれば 解決法1
だと思っています。
あるいは、 配列データの有無によってレンダリングしないコンポーネント
という明示的なコンポーネントを用意しそれを踏まえた設計にするのも良いのかもしれません。
<ConditionalComponent exists={items}>
<section className={indexStyles.section}>
<ItemList items={items} />
</section>
</ConditionalComponent>
Discussion