如何在React JSX中实现类似ERB的列表分栏渲染?
在JSX中实现列表分栏的方案
背景
我习惯传统后端开发风格,使用ERB这类服务端模板时,可编写如下代码实现列表分栏:
@items = ['South Africa', 'Zimbabwe', 'Zambia', 'Egypt', 'Malawi']
<div class="columns"> <div class="column"> <%- @items.each_with_index do |item, index| -%> <% if (index >= @items.length / 2) %> </div><div class="column"> <% end %> <div class="menu-item"> <%= item %> </div> <%- end -%> </div> </div>
这段代码最终能生成合法HTML文档,但编译阶段代码本身并不合法。而React JSX不支持此类写法,比如以下手动编写的尝试存在语法错误:
const items = ['South Africa', 'Zimbabwe', 'Zambia', 'Egypt', 'Malawi']
<div className="columns"> <div className="column"> {items.map((item, index) => { {(index === items.length / 2) ? </div><div className="column"> : null} <div className="menu-item"> {item} </div> })} </div> </div>
使用dangerouslySetInnerHTML也无法满足需求,因为需要额外包裹元素,会破坏原有结构。请问如何在JSX中实现该列表分栏功能?
可行实现方案
方案1:先分割数组再渲染
JSX要求结构必须闭合且符合语法,最直接的方式是先把原数组拆分为两个子数组,再分别渲染对应的列:
const items = ['South Africa', 'Zimbabwe', 'Zambia', 'Egypt', 'Malawi']; // 向上取整,保证前栏元素数量不小于后栏 const splitIndex = Math.ceil(items.length / 2); const firstColumnItems = items.slice(0, splitIndex); const secondColumnItems = items.slice(splitIndex);
<div className="columns"> <div className="column"> {firstColumnItems.map(item => ( <div className="menu-item" key={item}> {item} </div> ))} </div> <div className="column"> {secondColumnItems.map(item => ( <div className="menu-item" key={item}> {item} </div> ))} </div> </div>
方案2:动态生成列数组(支持多栏扩展)
如果需要支持更多分栏,可以先把原数组按指定栏数拆分为多维数组,再批量渲染列:
const items = ['South Africa', 'Zimbabwe', 'Zambia', 'Egypt', 'Malawi']; const columnCount = 2; // 可按需修改为3、4等 const itemsPerColumn = Math.ceil(items.length / columnCount); const columns = []; for (let i = 0; i < columnCount; i++) { columns.push(items.slice(i * itemsPerColumn, (i + 1) * itemsPerColumn)); }
<div className="columns"> {columns.map((columnItems, idx) => ( <div className="column" key={idx}> {columnItems.map(item => ( <div className="menu-item" key={item}> {item} </div> ))} </div> ))} </div>
方案3:用React.Fragment处理连续元素(不推荐)
如果想要接近ERB“中途切换容器”的写法,可利用React.Fragment(或简写<>)包裹逻辑,但这种写法可读性差、易出错,仅作兼容场景参考:
<div className="columns"> {items.map((item, index) => { const splitIndex = Math.floor(items.length / 2); return ( <> {/* 在分割点处渲染新列的开头 */} {index === splitIndex && <div className="column" />} <div className="menu-item" key={item}> {item} </div> {/* 在分割点前渲染前列的结尾 */} {index === splitIndex - 1 && </div>} </> ); })} </div>
内容的提问来源于stack exchange,提问作者Paul Watson
相关产品推荐
相关产品推荐

