Реализация бесконечной прокрутки в React с клиентом Apollo - PullRequest
1 голос
/ 24 сентября 2019

В моем приложении NextJS у меня есть компонент PostList.jsx, который выглядит следующим образом:

import { useQuery } from '@apollo/react-hooks';
import Typography from '@material-ui/core/Typography';
import { NetworkStatus } from 'apollo-client';
import gql from 'graphql-tag';
import getPostsQuery from '../../apollo/schemas/getPostsQuery.graphql';
import Loading from './Loading';
import Grid from '@material-ui/core/Grid';
import PostPreview from './PostPreview';
import withStyles from '@material-ui/core/styles/withStyles';
import React, { useLayoutEffect } from 'react';

const styles = (theme) => ({
  root: {
    padding: theme.spacing(6, 2),
    width: '100%',
  },
});

export const GET_POSTS = gql`${getPostsQuery}`;

export const getPostsQueryVars = {
  start: 0,
  limit: 7,
};

const PostsList = (props) => {
  const { classes } = props;
  const {
    loading,
    error,
    data,
    fetchMore,
    networkStatus,
  } = useQuery(
    GET_POSTS,
    {
      variables: getPostsQueryVars,
      // Setting this value to true will make the component rerender when
      // the "networkStatus" changes, so we'd know if it is fetching
      // more data
      notifyOnNetworkStatusChange: true,
    },
  );

  const loadingMorePosts = networkStatus === NetworkStatus.fetchMore;

  const loadMorePosts = () => {
    fetchMore({
      variables: {
        skip: posts.length
      },
      updateQuery: (previousResult, { fetchMoreResult }) => {
        if (!fetchMoreResult) {
          return previousResult
        }
        return Object.assign({}, previousResult, {
          // Append the new posts results to the old one
          posts: [...previousResult.posts, ...fetchMoreResult.posts]
        })
      }
    })
  };

  const scrollFunction = () => {
    const postsContainer = document.getElementById('posts-container');
    if (postsContainer.getBoundingClientRect().bottom <= window.innerHeight) {
      console.log('container bottom reached');
    }
  };

  useLayoutEffect(() => {
    document.addEventListener('scroll', scrollFunction);
    scrollFunction();
    // returned function will be called on component unmount
    return () => {
      document.removeEventListener('scroll', scrollFunction);
    };
  }, []);

  if (error) return <div>There was an error!</div>;
  if (loading) return <Loading />;

  const { posts, postsConnection } = data;
  const areMorePosts = posts.length < postsConnection.aggregate.count;

  return (
    <Grid item className={classes.root}>
      <Grid container spacing={2} direction="row" id="posts-container">
        {posts.map((post) => {
          return (
            <Grid item xs={12} sm={6} md={4} lg={3} xl={2} className={`post-preview-container`}>
              <PostPreview
                title={post.title}
                excerpt={post.excerpt}
                thumbnail={`https://i.schandillia.com/d/${post.thumbnail.hash}${post.thumbnail.ext}`}
              />
            </Grid>
          );
        })}
      </Grid>
      {areMorePosts && (
        <button onClick={() => loadMorePosts()} disabled={loadingMorePosts}>
          {loadingMorePosts ? 'Loading...' : 'Show More'}
        </button>
      )}
    </Grid>
  );
};

export default withStyles(styles)(PostsList);

Как вы можете видеть, этот компонент извлекает документы из базы данных с помощью запроса GraphQL с помощью клиента Apollo и отображаетих нумерация страницРазбивка на страницы определяется объектом getPostsQueryVars.Здесь, если вы прокрутите вниз и все еще есть доступные посты, вы получите кнопку, нажимающую, что будет загружен следующий набор постов.

Что мне здесь нужно, так это реализовать некоторые из них.вид бесконечной прокрутки и покончить с кнопкой вообще.До сих пор я добавил функцию компонента scroll к компоненту, используя перехватчики React, и могу подтвердить, что она срабатывает, как и ожидалось:

const scrollFunction = () => {
    const postsContainer = document.getElementById('posts-container');
    if (postsContainer.getBoundingClientRect().bottom <= window.innerHeight) {
      console.log('container bottom reached');
    }
  };

  useLayoutEffect(() => {
    document.addEventListener('scroll', scrollFunction);
    scrollFunction();
    return () => {
      document.removeEventListener('scroll', scrollFunction);
    };
  }, []);

Но как мне действовать дальше?Как добиться следующего, если container bottom is reached И areMorePosts равен true:

  1. Отображать <h4>Loading...</h4> прямо перед последним </Grid>?
  2. Запустить loadMorePosts() функция?
  3. удалить <h4>Loading...</h4> после завершения loadMorePosts()?
...