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

使用React.useMemo向react-table传递数组时,其中一个useMemo返回空数组导致useTable报错的问题求助

解决React.useMemo配合react-table时headerColumns为空的问题

看起来你遇到的核心问题是headerColumns的useMemo依赖配置错误,加上React的渲染时序特性,导致useTable拿到了空的列配置数组。让我们一步步拆解并解决:

问题根源分析

你当前的headerColumns是基于inputDataState生成的,但useMemo的依赖却是inputNames:

const headerColumns = React.useMemo(() => header, [inputNames]);

而inputDataState是在useEffect中更新的(依赖inputNames),useEffect的执行时机是在组件渲染完成之后。这就导致:当inputNames更新时,useMemo会先触发计算(此时inputDataState还没更新,header还是旧的空数组),之后useEffect才会更新inputDataState,但这时候headerColumns已经被传给useTable了,自然就拿到了空数组。

另外,colArrayData能正常工作是因为它直接依赖data,而data的处理逻辑没有经过useEffect,是同步计算的。

具体解决方案

1. 修正useMemo的依赖项 & 内联列生成逻辑

把header的生成逻辑移到useMemo内部,同时将依赖改为inputDataState——因为列配置完全由inputDataState决定:

const headerColumns = React.useMemo(() => {
  return inputDataState?.map((item, i) => {
    return { Header: item.name, accessor: `col${i}` };
  }) || []; // 兜底空数组,避免undefined
}, [inputDataState]);

这样只有当inputDataState更新时,headerColumns才会重新计算,确保拿到的是最新的列配置。

2. 给useTable的配置添加兜底值

即使做了上面的修改,在初始渲染时inputDataState可能还是空数组,所以给useTable的配置加上默认值,避免触发“Cannot read property of undefined”错误:

const tableInstance = useTable({
  headerColumns: headerColumns || [],
  colArrayData: colArrayData || []
});

3. 确保数据就绪后再初始化表格(可选但推荐)

因为你的组件中有多个异步数据请求(useSWR、useQuery),可以统一判断所有数据是否加载完成,再渲染表格相关的逻辑:

// 统一判断加载状态
const isAllLoaded = !loading && !!data && !!inputDataState.length;

if (!isAllLoaded) return <p>Loading</p>;

// 只有当所有数据就绪后,才初始化useTable
const tableInstance = useTable({ headerColumns, colArrayData });

修改后的完整代码片段

const fetchWithId = (url, id) => fetch(`${url}?id=${id}`).then((r) => r.json());
const fetcher = (url) => fetch(url).then((r) => r.json());
export default function FormData() {
  const { data: cookieData, error: cookieError } = useSWR("/api/cookie", fetcher);
  const Router = useRouter();
  const { data, error } = useSWR([`/api/data`, Router.query.id], fetchWithId);
  const test = `Bearer ${cookieData}`; // 用const代替var,更符合React规范
  const { loading, error: inputError, data: inputNames } = useQuery(
    INPUT_VALUES,
    {
      variables: { id: Router.query.id },
      context: {
        headers: { authorization: test },
      },
      skip: !test,
    }
  );
  const [inputDataState, setInputDataState] = useState([]);

  useEffect(() => {
    if (inputNames?.findFormByID?.formInputVals?.data) {
      const inputData = inputNames.findFormByID.formInputVals.data;
      const arr = [...inputData, { name: "Sheet Add" }];
      setInputDataState(arr);
    }
  }, [inputNames]);

  // 修正后的headerColumns useMemo
  const headerColumns = React.useMemo(() => {
    return inputDataState?.map((item, i) => {
      return { Header: item.name, accessor: `col${i}` };
    }) || [];
  }, [inputDataState]);

  const colArrayData = React.useMemo(() => {
    if (!data?.data) return [];
    const well = data.data.map((item, i) => {
      delete item.data["FormID"];
      delete item.data["user"];
      delete item.data["visible"];
      delete item.data["sheetName"];
      item.data["refId"] = item.ref["@ref"].id;
      const ok = Object.values(item.data);
      return ok.map((value, i) => {
        return { [`col${i}`]: value };
      });
    });
    return well?.map((a) => Object.assign({}, ...a)) || [];
  }, [data]);

  // 统一判断加载状态
  const isAllLoaded = !loading && !!data && inputDataState.length > 0;

  if (!isAllLoaded) return <p>Loading</p>;

  // 数据就绪后再初始化表格
  const tableInstance = useTable({ headerColumns, colArrayData });

  return (
    <Layout>
      <h1>{inputNames?.findFormByID.name}</h1>
      {/* 这里可以添加表格的渲染逻辑,比如用tableInstance的getTableProps等 */}
    </Layout>
  );
}

额外优化建议

  • 避免使用var,改用const/let,符合现代JS和React的编码规范;
  • 在useEffect中添加空值判断,避免inputNames未就绪时访问findFormByID导致报错;
  • 把colArrayData的处理逻辑也移到useMemo内部,让数据处理和缓存逻辑更集中。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.29 09:47:34