用GraphQL反应无限滚动

时间:2020-01-14 19:37:09

标签: reactjs typescript graphql next.js infinite-scroll

我正尝试通过无限滚动通过滚动的graphql连接我的页面以填充更多项目。目前,我遇到的问题是每次滚动时刷新页面,然后在到达hasMore max时出错。如果有人对如何使它起作用有任何想法,那将是巨大的帮助。我正在使用nextjs,typescript,graphql和reactjs无限滚动

import React, { useState } from 'react';
import gql from 'graphql-tag';
import InfiniteScroll from 'react-infinite-scroller';
import { useQuery } from '@apollo/react-hooks';
import { useRouter } from 'next/router';
import { withApollo } from '../../lib/apollo';

import { ErrorIndicator, LoadingIndicator, SubHeading } from '../../components/Atoms/';
import { Card, SubNav } from '../../components/Molecules/';
import { ArticleList, SixCards } from '../../components/Organisms/';
import { BREAKPOINT } from '../../constants/';
import { countryData } from '../../data/countryData';

export const queryCreator = (categories, count): any => gql`
{
  articles(lang: "en", categories: ["${categories}"], count: ${count}){
    id
    slug
    title
    content
    featuredImage
    creator {
      name
      id
    }
    categories {
      name
      id
    }
  }
}
`;

export const News: React.FC = () => {
  const [articleListState, updateArticleList] = useState({
    count: 8,
    articleList: []
  });

  const { count, articleList } = articleListState;
  const router = useRouter();
  const { categories } = router.query;
  const categoriesToQuery = categories ? categories.toString() : '';
  const formattedQuery = queryCreator(categoriesToQuery, count);
  const { loading, error, data } = useQuery(formattedQuery);

  if (error) {
    return <ErrorIndicator />;
  }

  if (loading) {
    return <LoadingIndicator />;
  }
  const { articles } = data;

  // updateArticleList({ count: 8, articleList: articles });

  const hasMore = () => count <= 13;
  const loadMore = () => {
    if (hasMore) {
      const addOne = count + 1;
      updateArticleList({ count: addOne, articleList: articles });
    }
  };

  const items = [];

  articleList.map((article) =>
    items.push(
      article && (
        <div className="article" key={article.id}>
          <Card key={article.id} article={article} size="short" />
          <style jsx>{`
            .article {
              padding: 6px 0;
              margin: 0 auto;
            }
          `}</style>
        </div>
      )
    )
  );
  console.log(items, count);
  return (
    <main className="articles-container">
      <SubNav />
      <SixCards articles={articles} />
      <div className="article-list">
        <InfiniteScroll
          pageStart={0}
          loadMore={loadMore}
          hasMore={hasMore()}
          loader={
            <div key={0}>
              <SubHeading title="Loading . . ." Tag="h5" />
            </div>
          }
        >
          {items}
          {/* <ArticleList articles={articleList} /> */}
        </InfiniteScroll>
      </div>
      <style jsx>{`
        main.articles-container {
          min-height: 80vh;
          display: flex;
          flex-direction: column;
          width: 80%;
          margin: 0 auto;
        }

        .article-list {
          display: flex;
          flex-direction: column;
          justify-content: center;
          // width: 100%;
          max-width: 1020px;
          margin: 10px auto;
          align-self: center;
        }

        @media only screen and (max-width: ${BREAKPOINT}px) {
          main.articles-container {
            width: 90%;
          }
        }
      `}</style>
    </main>
  );
};

export default withApollo(News);

1 个答案:

答案 0 :(得分:0)

我的查询使用 React、Typescript、GraphQL、apollo/client 和 graphql-tag。

在此示例中,我们将创建 RepoList 组件。此组件将使用 Github 的 GraphQl 使用 searchTerm 获取存储库列表。

import React from 'react';
import { useDebounce } from 'use-debounce';
import gql from 'graphql-tag';
import { useQuery } from '@apollo/client';
import InfiniteScroll from 'react-infinite-scroll-component';

import RepositoryItem, { SkeletonRepositoryCard } from './RepositoryItem';


export const SEARCH_FOR_REPOS = gql`
  query($search_term: String!, $cursor: String) {
    search(query: $search_term, type: REPOSITORY, first: 15, after: $cursor) {
      repositoryCount
      edges {
        node {
          ... on Repository {
            id
            name
            description
            owner {
              login
              avatarUrl
            }
            stargazers {
              totalCount
            }
          }
        }
        cursor
      }
      pageInfo {
        endCursor
        hasNextPage
      }
    }
  }
`;

export interface RepoDetails {
  node: {
    id: string;
    name: string;
    description: string | null;
    owner: {
      login: string;
      avatarUrl: string;
    };
    stargazers: {
      totalCount: number;
    };
  };
}

interface SearchRepoData {
  search: {
    repositoryCount: string;
    edges: RepoDetails[];
    pageInfo: {
      endCursor: string;
      hasNextPage: boolean;
    };
  };
}

interface SearchRepoVars {
  search_term: string;
}

const RepositoryList: React.FC<{ searchTerm: string }> = ({ searchTerm }) => {
  const classes = useStyles();
  const [expandedRepo, setExpandedRepo] = React.useState<number | null>(null);
  const [nextCursor, setNextCursor] = React.useState<string | null>(null);
  const [deboucedSearchKey] = useDebounce(searchTerm, 500);

  const { data, loading, error, fetchMore } = useQuery<
    SearchRepoData,
    SearchRepoVars
  >(SEARCH_FOR_REPOS, { variables: { search_term: deboucedSearchKey } });

  // Reset every time the data change
  // We make sure the repos shrink when we make a new search
  React.useEffect(() => {
    setExpandedRepo(null);
  }, [data]);

  const handleFetchMore = () => {
    console.log('next');
    if (data)
      fetchMore({
        variables: {
          cursor: data.search.pageInfo.endCursor,
          search_term: searchTerm,
        },
      });
  };

  if (loading && searchTerm.length > 1)
    return (
     < ... render loader ... >
    );

  if (error)
    return (
      < ... render error ... >
    );

  if (!loading && searchTerm.length && data && !data.search.repositoryCount)
    return (
      < ... Render No Repo ... >
    );

  if (data && data.search.repositoryCount)
    return (
      <InfiniteScroll
        dataLength={data.search.edges.length}
        next={handleFetchMore}
        hasMore={data.search.pageInfo.hasNextPage}
        loader={<h4>Loading...</h4>}
        endMessage={
          <p style={{ textAlign: 'center' }}>
            <b>Yay! You have seen it all</b>
          </p>
        }
      >
          {data &&
            data.search.edges.map((repo, i) => (
              < ... render repo card ... >
            ))}
      </InfiniteScroll>
    );

  return <div>Please search for repos</div>;
};

export default RepositoryList;