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

React+JavaScript中fetch获取API数据JSON解析渲染报错排查

报错根因

你遇到的是典型的类型不匹配+React Hook使用错误,具体问题点:

  • 初始状态类型错误:page.js中存储接口返回值的data初始值设为空对象{},但子组件CustomerListResults的PropTypes明确要求customers为数组类型。组件首次渲染时接口请求尚未返回,传入子组件的是空对象,对象上不存在slice/map/length这类数组原型方法,直接触发customers.slice is not a function运行时错误。
  • useEffect缺少依赖项:当前useEffect没有传入第二个依赖数组参数,每次组件重渲染都会重新执行fetch请求,触发无限重渲染循环。
  • 遍历逻辑变量引用错误:list_component.js中map遍历的单个列表项参数是customer,但渲染单元格内容、设置key值时错误引用了整个数组customers的属性,就算数据类型正确也会渲染出错误内容。
  • 错误处理逻辑失效:setError不支持多参数传入,当前错误状态赋值逻辑无法正确保存接口报错信息,且没有捕获fetch请求的网络异常。
  • 分页逻辑缺失:slice起始索引固定为0,翻页后列表内容不会更新。
修复代码

修正后的page.js

function Customers(props) {
  // 初始值设为空数组,匹配子组件的props类型要求
  const [data, setData] = useState([]);
  const [error, setError] = useState(null);

  useEffect(() => {
    fetch("http://localhost:8080/contacts")
      .then((response) => {
        // 用response.ok覆盖所有2xx成功状态判断,比单独判断status===200更严谨
        if (!response.ok) {
          throw new Error(`Invalid response: ${response.status}`);
        }
        return response.json();
      })
      .then((json) => {
        // 双重兜底:如果接口返回值不是数组,默认存入空数组避免类型错误
        // 注意:如果接口返回结构是{data: [...]}这类包裹格式,这里要取对应数组字段,比如setData(Array.isArray(json.data) ? json.data : [])
        setData(Array.isArray(json) ? json : []);
      })
      .catch((err) => {
        // 统一捕获网络错误、接口状态错误存入error状态
        setError(err);
      });
  }, []); // 传入空依赖数组,仅在组件首次挂载时执行一次请求

  if (error !== null) {
    return <div>Error: {error.message}</div>;
  }
  return (
    <DashboardLayout>
      <>
        <Head>
          <title>Data</title>
        </Head>
        <Box
          component="main"
          sx={{
            flexGrow: 1,
            py: 8
          }}
        >
          <Container maxWidth={false}>
            <Box sx={{ mt: 3 }}>
              <CustomerListResults customers={data} />
            </Box>
          </Container>
        </Box>
      </>
    </DashboardLayout>
  );
}

export default Customers;

修正后的list_component.js遍历逻辑

只需要修改TableBody和TablePagination部分的逻辑即可,其余代码保持不变:

<TableBody>
  {/* 修正slice起始索引,适配分页切换 */}
  {customers.slice(page * limit, page * limit + limit).map((customer) => (
    <TableRow
      hover
      key={customer.id} // 取当前遍历项的唯一标识作为key
    >
      <TableCell>
        <Box
          sx={{
            alignItems: 'center',
            display: 'flex'
          }}
        >
          <Typography
            color="textPrimary"
            variant="body2"
          >
            {customer.created_date} {/* 取当前遍历项的属性,而非整个数组 */}
          </Typography>
        </Box>
      </TableCell>
      <TableCell>
        {customer.user_idx} {/* 同上,使用单个遍历项变量 */}
      </TableCell>
    </TableRow>
  ))}
</TableBody>
{/* 分页部分count直接取数组长度即可,无需修改 */}
<TablePagination
  component="div"
  count={customers.length}
  onPageChange={handlePageChange}
  page={page}
  rowsPerPage={limit}
/>

注意:如果通过Postman验证接口时发现返回的JSON不是直接的数组结构,不要把整个响应对象直接存入state,必须取到实际存放联系人数组的字段再赋值。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.27 20:48:22