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

React中向子组件传递状态数组时变量未定义问题排查

React向子组件传数组变undefined?问题出在这几个地方

问题根源

  1. 异步状态更新踩坑:你在ListAttributions和ListInteractions里调用setAttributionsData后,马上就用attributionsData.map渲染,但React的setState是异步的,这时候状态还没更新,用的还是初始的空数组。而且父组件状态更新会触发重新渲染,每次渲染都会重新创建这两个嵌套组件,导致useQuery反复执行,陷入不必要的循环。
  2. 数组判断完全错了:attributionsData !== []这种写法永远是true——因为两个空数组是不同的引用对象。这就导致TabbedResults一开始就会被渲染,而这时候API数据还没加载完,自然拿到的是空数组或者undefined。
  3. 嵌套组件定义位置不对:把ListAttributions和ListInteractions写在ShowResults内部,每次父组件渲染都会重新生成这两个组件,不仅浪费性能,还会破坏useQuery的缓存机制,导致重复请求。

修复步骤

1. 父组件统一管API查询

把useQuery移到父组件里,直接在父组件拿数据,再传给子组件渲染,这样状态逻辑更清晰,也不会有异步更新的问题。

2. 改对数组判断逻辑

用attributionsData.length > 0或者!!attributionsData来判断数据是否加载完成,别再对比数组引用了。

3. 把嵌套组件移到父组件外面

避免每次父组件渲染都重新创建组件,保证组件稳定还能省性能。

修复后的代码

import { useQuery } from '@apollo/client';
import PropTypes from 'prop-types';

// 把列表组件移到外部,避免重复创建
function ListAttributions({ attributionsData, pk }) {
  return (
    <div>
      {attributionsData.map(({ sk, nominal, organisation, attribution, file_name, datetime_added, exhibit }, index) => (
        <AttributionsCard 
          key={index}
          Sk={sk}
          Nominal={nominal}
          Organisation={organisation}
          Attribution={attribution}
          FileName={file_name}
          FoundInsidePhone={file_name.match(/[0-9]+/g)}
          DateTimeAdded={datetime_added}
          Exhibit={exhibit}
          Pk={pk}
        />
      ))}
    </div>
  );
}

function ListInteractions({ interactionsData, pk }) {
  return (
    <div>
      {interactionsData.map(({ direction, interaction, partner, duration, datetime, exhibit, organisation, file_name, datetime_added }, index) => (
        <InteractionsCard 
          key={index}
          Interaction={interaction}
          Direction={direction}
          Partner={partner}
          Duration={duration}
          DateTime={datetime}
          Exhibit={exhibit}
          Organisation={organisation}
          FileName={file_name}
          DateTimeAdded={datetime_added}
          Pk={pk}
        />
      ))}
    </div>
  );
}

const ShowResults = props => {
  const { pk } = props;

  // 父组件统一执行两个API查询
  const { 
    loading: attributionsLoading, 
    error: attributionsError, 
    data: attributionsQueryData 
  } = useQuery(GET_ATTRIBUTIONS, { variables: { pk } });
  
  const { 
    loading: interactionsLoading, 
    error: interactionsError, 
    data: interactionsQueryData 
  } = useQuery(GET_INTERACTIONS, { variables: { pk } });

  // 直接从查询结果取数据,没数据就用空数组兜底
  const attributionsData = attributionsQueryData?.listAttributions?.items || [];
  const interactionsData = interactionsQueryData?.listInteractions?.items || [];

  // 处理全局加载状态
  if (attributionsLoading || interactionsLoading) {
    return <h2>LOADING...</h2>;
  }

  // 处理错误
  if (attributionsError || interactionsError) {
    return `Error! ${attributionsError?.message || interactionsError?.message}`;
  }

  return (
    <div style={{display: "inline-grid", inlineSize: "max-content", width: "100%"}}>
      <center>
        <ListAttributions attributionsData={attributionsData} pk={pk}/>
        <ListInteractions interactionsData={interactionsData} pk={pk}/>
      </center>
      <br/>
      <br />
      {/* 等两个数据都加载完成再渲染TabbedResults */}
      {attributionsData.length > 0 && interactionsData.length > 0 && (
        <TabbedResults pk={pk} attributionsData={attributionsData} interactionsData={interactionsData} />
      )}
      <br />
      <br />
    </div>
  );
}

ShowResults.propTypes = {
  pk: PropTypes.string
};

export default ShowResults

补充说明

  • 如果之后需要修改数据(比如筛选、排序),再用useState存处理后的数据;如果只是展示,直接用查询返回的数据就行,少搞不必要的状态。
  • useQuery自带缓存,移到父组件后能更好地利用缓存,避免重复请求。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.19 10:40:25