【问题标题】:Next.JS Static site generation and client-side Apollo fetchingNext.JS 静态站点生成和客户端 Apollo 获取
【发布时间】:2021-04-19 05:13:14
【问题描述】:

我正在利用 Next.JS SSG 来提高我网站的加载速度。关键是我需要在客户端获取一些数据,因为它属于登录用户。

假设我们有一个类似 YouTube 的网站,所以我们将拥有 Video page 和一个侧边栏组件,即 VideoRelated

我将通过在getStaticProps 使用VideoQuery 来生成Video page 静态。它还将获取(客户端)用户登录的完成状态,所以它会是这样的:

export const VideoRelatedList = ({ videoId, relatedVideos }) => {
  const { data } = useViewerVideoStatusQuery({ variables: { videoId } });
  const relatedVideosViewerStatus = data?.videos;

  const videos = React.useMemo(() => {
    if (!relatedVideosViewerStatus) return relatedVideos;

    return relatedVideos.map((relatedVideo, index) => {
      const viewerHasCompleted = relatedVideosViewerStatus[index]?.id === relatedVideo.id && relatedVideosViewerStatus[index]?.viewerHasCompleted;
      return { ...relatedVideo, viewerHasCompleted };
    });
  }, [relatedVideosViewerStatus, relatedVideos]);

  return (
    <ol>
      {videos.map(({ id, name, viewerHasCompleted }, index) => (
        <li key={id}>
          {name} - {viewerHasCompleted && 'COMPLETED!'}
        </li>
      ))}
    </ol>
  );
};

结合这两种数据的最佳方法是什么?

目前,我正在做的是通过使用 React.memo 将两者结合起来,但我不确定这是否是最佳做法,或者是否有更好的方法来实现我想要的。

【问题讨论】:

  • 为了更好地理解,VideoList 正在获取相关视频作为道具。那么为什么这个组件里面有查询呢?
  • 因为 relatedVideo 属性将是页面传递给组件的内容,该组件是静态站点生成的。但是useViewerVideoStatusQuery获取的是访问该页面的用户相关的视频状态,明显是登录状态,建站时取不到。

标签: javascript reactjs graphql next.js apollo


【解决方案1】:

tl;dr 这似乎是一个不错的方法,您可能不需要useMemo

Next.js 文档在客户端数据获取方面有 a very brief blurb。它看起来,基本上,就像你正在做的一样,除了他们推广他们的swr 库以获取通用数据。

由于您使用的是 GraphQL 和 Apollo,我将假设 useViewerVideoStatusQuery 扩展了 useQuery hook from @apollo/client

你提到:

目前,我正在做的是通过使用 React.memo 将两者结合起来,但我不确定这是否是最佳实践,或者是否有更好的方法来实现我想要的。

请耐心等待,我将尝试确认我对您的用例的理解。如果我没看错,看起来VideoRelatedList 组件大部分可以是完全静态的,看起来这就是你所做的——如果没有加载额外的数据,它只是使用通过 SSG 构建的 props 传递的数据。这很好,应该给你最快的First Contentful Paint (FCP) & Largest Contentful Paint (LCP)

如果没有额外的数据,组件将如下所示:

export const VideoRelatedList = ({ videoId, relatedVideos }) => {
  return (
    <ol>
      {relatedVideos.map(({ id, name, viewerHasCompleted }, index) => (
        <li key={id}>
          {name} - {viewerHasCompleted && 'COMPLETED!'}
        </li>
      ))}
    </ol>
  );
};

现在您另外想要合并用户数据,以提供告诉用户哪些视频是完整的功能。您通过在页面最初使用 Apollo 呈现后获取数据来执行此操作。

我要做的主要更改(除非relatedVideos 数组很大或者我错过了一些昂贵的操作)是删除useMemo,因为考虑到performance optimizations aren't free,它可能不会在这里节省大量成本.

您还可以从 useQuery 挂钩中实现 loadingerror 属性。该组件可能类似于:

export const VideoRelatedList = ({ videoId, relatedVideos }) => {
  const { data, loading, error } = useViewerVideoStatusQuery({ variables: { videoId } });

  if (loading) {
    <ol>
      {relatedVideos.map(({ id, name, viewerHasCompleted }, index) => (
        <li key={id}>
          {name} - ...checking status...
        </li>
      ))}
    </ol>
  }

  if (error || !data.videos) {
    return (
      <ol>
        {videos.map(({ id, name, viewerHasCompleted }, index) => (
          <li key={id}>
            {name}
          </li>
        ))}
      </ol>
    );
  }

  const videos = relatedVideos.map((relatedVideo, index) => {
      const viewerHasCompleted = relatedVideosViewerStatus[index]?.id === relatedVideo.id && relatedVideosViewerStatus[index]?.viewerHasCompleted;
      return { ...relatedVideo, viewerHasCompleted };
    });

  return (
    <ol>
      {videos.map(({ id, name, viewerHasCompleted }, index) => (
        <li key={id}>
          {name} - {viewerHasCompleted && 'COMPLETED!'}
        </li>
      ))}
    </ol>
  );
};

同样,除了没有useMemo 和一些重新排列之外,这与您所做的并没有太大的不同。希望看到关于该主题的另一种观点至少会有所帮助。

我发现一些关于该主题的参考资料:

【讨论】:

    猜你喜欢
    • 2013-05-04
    • 2021-04-21
    • 1970-01-01
    • 1970-01-01
    • 2021-03-29
    • 2021-01-20
    • 2021-10-12
    • 2020-07-22
    • 2019-04-28
    相关资源
    最近更新 更多