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

React JSX array.map仅遍历首项 初始渲染无显示问题

问题根因

你的代码存在3个核心问题,刚好对应你观察到的三个异常现象:

  • 直接修改State原引用,React无法检测更新
    你在onCreateRunLocation里写let tempArray = runs,因为数组是引用类型,tempArray和state里的runs指向同一块内存,直接push修改后调用setRuns(tempArray),React浅比较时会发现传入的引用和旧state完全一致,不会触发重渲染。你控制台能看到runs里有3个对象,是因为控制台打印对象时打印的是引用地址,你后续直接修改原数组的操作会同步更新之前打印的日志内容,不代表React认可了这次state更新。
  • 渲染逻辑的map没有返回值,页面无法输出内容
    你在组件最终return的runs.map回调里用了花括号包裹代码,但是没有returnrenderLocation的执行结果,map最终返回的全是undefined,就算runs里有数据,页面也什么都显示不出来。
  • 并发请求触发闭包陷阱,State更新互相覆盖
    你在useEffect里同时发起3个数据请求,每个请求的onSnapshot回调触发时,闭包里捕获到的runs都是当次渲染的初始值(也就是空数组)。每个回调执行时都是往自己捕获到的空数组里push1个元素就调用setRuns,后执行的setRuns会直接覆盖前一次的更新结果,最终state里只会保留最后一次更新的1个元素,所以你看到map只遍历第一个元素。加上前面说的直接修改state不触发重渲染,组件render执行两次(初始挂载1次,第一次setRuns触发1次)之后就不会再更新了。
  • 其他不规范写法(非核心故障原因):arrangeProducts和useEffect里错误用map做纯遍历操作,map的设计用途是返回映射后的新数组,纯遍历场景应该用forEach;另外useEffect没有收集onSnapshot的取消订阅函数,组件卸载时会造成内存泄漏。

为什么修改代码热更新后页面就正常?
因为你保存代码触发热更新时,React会强制重渲染组件,此时你之前直接修改的runs数组已经被塞入了3个元素,重渲染时能拿到完整数据,所以页面能正常显示,但这只是歪打正着,首次加载时的逻辑问题依然存在。

修复后代码

状态与数据请求逻辑

const [runs, setRuns] = useState([])

useEffect(() => {
  // 收集所有订阅取消方法
  const unsubscribes = []
  machineDocIDs.forEach(item => {
    unsubscribes.push(fetchProductData(item))
  })
  // 组件卸载时统一取消订阅,避免内存泄漏
  return () => unsubscribes.forEach(unsub => unsub())
},[machineDocIDs])

function fetchProductData(id) {
  const productsCollectionRef = collection(db, "RunSheets", "All Runs", "Open", runID, "machines", id, "products")
  const q = query(productsCollectionRef)
  const productData = []
  const IDs = []
  const unsubscribe = onSnapshot(q, (querySnapshot) => {
    querySnapshot.forEach((doc) => {
      productData.push(doc.data())
      IDs.push(doc.id)
    })
    onCreateRunLocation(machines[machineDocIDs.indexOf(id)].machine_name, productData)
  })
  return unsubscribe
}

function onCreateRunLocation(machine, productData) {
  const loc = {machine_id: machine, products: arrangeProducts(productData)}
  // 用函数式更新拿到最新state,返回全新数组,不修改原state
  setRuns(prevRuns => [...prevRuns, loc])
}

function arrangeProducts(productData) {
  // 直接用map返回映射后的新数组,不需要额外声明数组push
  return productData.map(item => ({
    name: item.flavors, 
    quantity_ordered: item.quantity_ordered
  }))
}

渲染逻辑

function renderProduct(product) {
  return (
    <tr>
      <th>{product.name}</th>
      <th>{product.quantity_ordered}</th>
    </tr>
  )
}

function renderLocation(location) {
  return (
    <Card>
      <Card.Title>{location.machine_id}</Card.Title>
      <Card.Body>
        <Table>
          <tbody>
            {location.products.map(item => renderProduct(item))}
          </tbody>
        </Table>
      </Card.Body>
    </Card>
  )
}

return (
  <Container>
    {/* 箭头函数直接返回渲染结果,列表添加唯一key,实际项目请用item.machine_id代替index */}
    {runs.map((item, index) => (
      <React.Fragment key={index}>
        {renderLocation(item)}
      </React.Fragment>
    ))}
  </Container>
)

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.01 01:39:18