В моем приложении 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
:
- Отображать
<h4>Loading...</h4>
прямо перед последним </Grid>
? - Запустить
loadMorePosts()
функция? - удалить
<h4>Loading...</h4>
после завершения loadMorePosts()
?