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

HygraphCMS数据获取报错:TypeError无法读取postsConnection属性

从HygraphCMS获取数据时出现TypeError: Cannot read properties of undefined (reading 'postsConnection')

我花了好几个小时尝试解决从HygraphCMS获取数据的问题,但毫无进展。运行代码时抛出如下错误,但直接在GraphQL中执行查询语句能得到正常结果,恳请帮忙排查问题。

相关代码

import { request } from "graphql-request";
import Link from "next/link";
import { useState } from "react";
import useSWR from "swr";

const fetcher = (endpoint, query, variables) => request(endpoint, query, variables);

export const getStaticProps = async () => {
  const data = await fetcher(
    "https://api-eu-central-1-shared-euc1-02.hygraph.com/v2/clh4qcnhy5i5r01um1qbo28dj/master",
    `query getPosts {
      postsConnection(orderBy: date_DESC, first: 2, skip: 0) {
        edges {
          node {
            id
            title
            date
            slug
            description
            author {
              name
            }
          }
        }
        pageInfo {
          hasNextPage
          hasPreviousPage
          pageSize
        }
      }
    }`,
  );

  return {
    props: {
      posts: data,
    },
  };
};

export default function BlogPage({ posts }) {
  console.log(posts);
  const [searchValue, setSearchValue] = useState("");
  const [skip, setSkip] = useState(0);
  const { data, error } = useSWR(
    [
      "https://api-eu-central-1-shared-euc1-02.hygraph.com/v2/clh4qcnhy5i5r01um1qbo28dj/master",
      `query getPosts($searchValue: String $skip: Int) {
        postsConnection(orderBy: date_DESC, where: {title_contains: $searchValue}, first: 2, skip: $skip) {
          edges {
            node {
              id
              title
              date
              slug
              description
              id
              author {
                name
              }
            }
          }
          pageInfo{
            hasNextPage
            hasPreviousPage
            pageSize
          }
        }
      }
      
  `,
      searchValue,
      skip,
    ],
    (endpoint, query) => fetcher(endpoint, query, { searchValue, skip }),
    { initialData: posts, revalidateOnFocus: true },
  );

  if (error) {
    return (
      <div>
        <h2>There was an error with the data fetching</h2>
      </div>
    );
  }
  return (
    <div className="max-w-3xl mx-auto px-4 sm:px-6 lg:px-0">
      <h1 className="text-5xl text-gray-600 font-serif mb-6 font-bold">The Blog</h1>
      <div>
        <input
          type="text"
          value={searchValue}
          placeholder="Search blog posts"
          className="focus:outline-none mb-6 focus:ring-2 focus:ring-gray-900 w-full rounded-lg border h-10 pl-4 text-lg text-gray-800 border-gray-200"
          onChange={(event) => setSearchValue(event.target.value)}
        />
      </div>
      <div>
        {data.postsConnection?.edges?.map((post) => (
          <div key={post.node.slug} className="grid grid-cols-1 md:grid-cols-4 py-6">
            <div className="mb-2 md:mb-0 md:col-span-1">
              <p className="text-gray-600 text-sm">{new Date(post.node.date).toDateString()}</p>
            </div>
            <div className="md:col-span-3">
              <Link href={`/blog/${post.node.slug}`}>
                <a className="text-2xl font-semibold text-gray-900 hover:text-gray-700 transition-colors duration-300">
                  {post.node.title}
                </a>
              </Link>
              <p className="text-gray-700 leading-relaxed">{post.node.description}</p>
              <div className="text-sm text-gray-900 font-semibold mt-1">
                {post.node.author.name}
              </div>
            </div>
          </div>
        ))}
      </div>
      <div className="flex space-x-5 justify-center items-center mt-10">
        <div>
          <button
            onClick={() => {
              setSkip(skip - 2);
            }}
            disabled={!data.postsConnection.pageInfo.hasPreviousPage}
            className="bg-indigo-700 w-20 text-white px-3 py-1 rounded-md disabled:bg-gray-400 disabled:text-gray-800">
            Previous
          </button>
        </div>
        <div>
          <button
            onClick={() => {
              setSkip(skip + 2);
            }}
            disabled={!data.postsConnection.pageInfo.hasNextPage}
            className="bg-indigo-700 w-20 text-white px-3 py-1 rounded-md disabled:bg-gray-400 disabled:text-gray-800">
            Next
          </button>
        </div>
        <div className="text-gray-700">Total Pages: {data.postsConnection.pageInfo.pageSize}</div>
      </div>
    </div>
  );
}

错误信息

TypeError: Cannot read properties of undefined (reading 'postsConnection')

错误来源:

Source
pages/blog/index.js (105:14) @ BlogPage

  103 | </div>
  104 | <div>
> 105 |   {data.postsConnection?.edges?.map((post) => (
      |        ^
  106 |     <div key={post.node.slug} className="grid grid-cols-1 md:grid-cols-4 py-6">
  107 |       <div className="mb-2 md:mb-0 md:col-span-1">
  108 |         <p className="text-gray-600 text-sm">{new Date(post.node.date).toDateString()}</p>

GraphQL查询结果

getStaticProps的查询结果

{
  "data": {
    "postsConnection": {
      "edges": [
        {
          "node": {
            "id": "clh4w8dhkaufl0bw1jux7ek2y",
            "title": "React Toast Component and Custom Hook using Tailwindcss",
            "date": "2023-05-01T13:45:00+00:00",
            "slug": "react-toast-component-and-custom-hook-using-tailwindcss",
            "description": "Toasts are basically just notifications. When you have a complex application or you need a lot of input",
            "author": {
              "name": "HerminioHenriques"
            }
          }
        },
        {
          "node": {
            "id": "clh4tgvgxac9f0bt92iyuni90",
            "title": "Dummy markdown post",
            "date": "2023-05-01T12:26:00+00:00",
            "slug": "dummy-markdown-post",
            "description": "How to use markdown language",
            "author": {
              "name": "HerminioHenriques"
            }
          }
        },
        {
          "node": {
            "id": "clh4var6yaqwl0bw1aefk73rc",
            "title": "Next.js, Typescript and ESLint Starter Configuration",
            "date": "2023-04-25T17:00:00+00:00",
            "slug": "nextjs-typescript-and-eslint-starter-configuration",
            "description": "This blog post will guide you through configuring a new Next.js project, ready to go with Typescript, ESLint and pre-commit checking with Husky",
            "author": {
              "name": "HerminioHenriques"
            }
          }
        }
      ],
      "pageInfo": {
        "hasNextPage": false,
        "hasPreviousPage": false,
        "pageSize": 3
      }
    }
  }
}

组件内查询的结果

{
  "data": {
    "postsConnection": {
      "edges": [
        {
          "node": {
            "id": "clh4tgvgxac9f0bt92iyuni90",
            "title": "Dummy markdown post",
            "date": "2023-05-01T12:26:00+00:00",
            "slug": "dummy-markdown-post",
            "description": "How to use markdown language",
            "author": {
              "name": "HerminioHenriques"
            }
          }
        }
      ],
      "pageInfo": {
        "hasNextPage": false,
        "hasPreviousPage": false,
        "pageSize": 1
      }
    }
  }
}

问题排查与解决

核心原因

  1. 数据结构不匹配:getStaticProps返回的posts是{ data: { postsConnection: ... } },但useSWR请求返回的data直接包含postsConnection字段,导致initialData的结构和后续请求返回的结构不一致,触发报错。
  2. 未处理空值场景:分页按钮和页码显示处直接访问data.postsConnection.pageInfo,没有做可选链保护,当data为undefined时会直接抛出错误。

解决步骤

1. 修正getStaticProps返回的结构

将getStaticProps中返回的posts改为data.data,让初始数据结构和useSWR请求返回的结构一致:

export const getStaticProps = async () => {
  const data = await fetcher(/* ... */);

  return {
    props: {
      posts: data.data, // 这里改为data.data
    },
  };
};

2. 给分页相关代码添加可选链保护

在分页按钮和页码显示处添加可选链,避免data为undefined时触发报错:

// Previous按钮
disabled={!data?.postsConnection?.pageInfo?.hasPreviousPage}

// Next按钮
disabled={!data?.postsConnection?.pageInfo?.hasNextPage}

// 页码显示
<div className="text-gray-700">Total Pages: {data?.postsConnection?.pageInfo?.pageSize}</div>

完成以上修改后,代码就能正确处理初始数据和后续请求数据,避免postsConnection未定义的报错。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.23 10:44:54