You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

React中使用.map()时如何在列表行之间添加图标

简便实现行间分隔的方案

不用折腾窗口尺寸计算,直接通过检测元素的垂直位置判断换行,下面是具体实现:

思路

利用DOM元素的offsetTop属性,当某个列表项的垂直位置比前一项高时,说明它是新行的第一个元素,此时在它前面插入分隔组件。同时监听窗口 resize 事件,确保窗口大小变化时重新检测换行位置。

代码实现

import React, { useState, useRef, useLayoutEffect, useCallback } from 'react'
import { ListItem } from './ListItem'
// 自定义分隔线组件,也可以用原生div替代
const Divider = () => <div style={{ width: '100%', height: 1, backgroundColor: '#ccc', margin: '8px 0' }} />

const REPORTS = [
    { id: 1, name: 'first report' },
    { id: 2, name: 'second report' },
    { id: 3, name: 'third report' },
    { id: 4, name: 'fourth report' },
    { id: 5, name: 'fifth report' },
    { id: 6, name: 'sixth report' },
    { id: 7, name: 'Kyles report' },
]

export const ReportsList = () => {
    const [reports, setReports] = useState(REPORTS)
    const [renderItems, setRenderItems] = useState([])
    const listRef = useRef(null)

    // 检测换行并生成带分隔符的列表
    const updateRenderList = useCallback(() => {
        if (!listRef.current) return
        const itemElements = Array.from(listRef.current.children)
        if (itemElements.length === 0) return

        const newRenderList = []
        let prevTop = itemElements[0].offsetTop

        // 先添加第一个列表项
        newRenderList.push(
            <ListItem key={reports[0].id} index={0} text={reports[0].name} />
        )

        // 遍历剩余项,判断是否需要插入分隔符
        for (let i = 1; i < reports.length; i++) {
            const currentTop = itemElements[i].offsetTop
            if (currentTop > prevTop) {
                newRenderList.push(<Divider key={`divider-${i}`} />)
            }
            newRenderList.push(
                <ListItem key={reports[i].id} index={i} text={reports[i].name} />
            )
            prevTop = currentTop
        }

        setRenderItems(newRenderList)
    }, [reports])

    // 初始渲染和窗口 resize 时更新列表
    useLayoutEffect(() => {
        updateRenderList()
        const handleResize = () => {
            // 防抖处理,避免频繁触发
            clearTimeout(window.resizeTimer)
            window.resizeTimer = setTimeout(updateRenderList, 100)
        }
        window.addEventListener('resize', handleResize)
        return () => window.removeEventListener('resize', handleResize)
    }, [updateRenderList])

    return (
        <div ref={listRef} className='boxList'>
            {/* 初始渲染用原列表,避免空内容 */}
            {renderItems.length > 0 ? renderItems : reports.map((report, index) => (
                <ListItem key={report.id} index={index} text={report.name} />
            ))}
        </div>
    )
}

补充说明

  1. 用useLayoutEffect代替useEffect,确保DOM更新后立刻检测位置,避免获取到不准确的offsetTop值
  2. 加入防抖处理,窗口 resize 时不会频繁触发更新,提升性能
  3. 分隔组件可根据需求自定义样式,无需额外依赖

内容的提问来源于stack exchange,提问作者Gabriel

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.07 19:20:59