mirror of
https://github.com/muerwre/vault-frontend.git
synced 2025-04-25 04:46:40 +07:00
made lab infinite loader with intersection observer
This commit is contained in:
parent
9dbc5739b7
commit
a553a06ac5
5 changed files with 63 additions and 24 deletions
|
@ -1,3 +1,3 @@
|
||||||
#REACT_APP_API_HOST=http://localhost:3334/
|
#REACT_APP_API_HOST=http://localhost:3334/
|
||||||
REACT_APP_API_HOST=https://pig.staging.vault48.org/
|
REACT_APP_API_HOST=https://pig.vault48.org/
|
||||||
REACT_APP_REMOTE_CURRENT=https://pig.staging.vault48.org/static/
|
REACT_APP_REMOTE_CURRENT=https://pig.vault48.org/static/
|
||||||
|
|
|
@ -1,14 +1,16 @@
|
||||||
import React, { FC } from 'react';
|
import React, { FC, useMemo } from 'react';
|
||||||
import Masonry from 'react-masonry-css';
|
import Masonry from 'react-masonry-css';
|
||||||
import styles from './styles.module.scss';
|
import styles from './styles.module.scss';
|
||||||
import { LabNode } from '~/components/lab/LabNode';
|
import { LabNode } from '~/components/lab/LabNode';
|
||||||
import { EMPTY_NODE, NODE_TYPES } from '~/redux/node/constants';
|
import { EMPTY_NODE, NODE_TYPES } from '~/redux/node/constants';
|
||||||
import { values } from 'ramda';
|
import { values } from 'ramda';
|
||||||
import { ILabNode } from '~/redux/lab/types';
|
import { ILabNode } from '~/redux/lab/types';
|
||||||
|
import { useLabPagination } from '~/utils/hooks/lab/useLabPagination';
|
||||||
|
|
||||||
interface IProps {
|
interface IProps {
|
||||||
isLoading: boolean;
|
isLoading: boolean;
|
||||||
nodes: ILabNode[];
|
nodes: ILabNode[];
|
||||||
|
onLoadMore: () => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
const breakpointCols = {
|
const breakpointCols = {
|
||||||
|
@ -28,7 +30,14 @@ const LoadingNode = () => (
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
|
||||||
const LabGrid: FC<IProps> = ({ isLoading, nodes }) => {
|
const LabGrid: FC<IProps> = ({ isLoading, nodes, onLoadMore }) => {
|
||||||
|
const columns = useMemo(() => Array.from(document.querySelectorAll(`.${styles.column}`)), [
|
||||||
|
isLoading,
|
||||||
|
nodes,
|
||||||
|
]);
|
||||||
|
|
||||||
|
useLabPagination(isLoading, columns, onLoadMore);
|
||||||
|
|
||||||
if (isLoading) {
|
if (isLoading) {
|
||||||
return (
|
return (
|
||||||
<Masonry
|
<Masonry
|
||||||
|
|
|
@ -10,6 +10,7 @@ div.wrap {
|
||||||
background-clip: padding-box;
|
background-clip: padding-box;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
padding: 0 $gap / 2;
|
padding: 0 $gap / 2;
|
||||||
|
margin-top: -$gap / 2;
|
||||||
|
|
||||||
@include tablet {
|
@include tablet {
|
||||||
padding: 0;
|
padding: 0;
|
||||||
|
|
|
@ -1,10 +1,10 @@
|
||||||
import React, { FC, useCallback, useEffect } from 'react';
|
import React, { FC, useCallback, useEffect, useMemo, useRef } from 'react';
|
||||||
import styles from './styles.module.scss';
|
import styles from './styles.module.scss';
|
||||||
import { Sticky } from '~/components/containers/Sticky';
|
import { Sticky } from '~/components/containers/Sticky';
|
||||||
import { Container } from '~/containers/main/Container';
|
import { Container } from '~/containers/main/Container';
|
||||||
import { LabGrid } from '~/containers/lab/LabGrid';
|
import { LabGrid } from '~/containers/lab/LabGrid';
|
||||||
import { useDispatch } from 'react-redux';
|
import { useDispatch } from 'react-redux';
|
||||||
import { labGetList, labGetStats } from '~/redux/lab/actions';
|
import { labGetList, labGetMore, labGetStats } from '~/redux/lab/actions';
|
||||||
import { Group } from '~/components/containers/Group';
|
import { Group } from '~/components/containers/Group';
|
||||||
import { LabHead } from '~/components/lab/LabHead';
|
import { LabHead } from '~/components/lab/LabHead';
|
||||||
import { LabStats } from '~/containers/lab/LabStats';
|
import { LabStats } from '~/containers/lab/LabStats';
|
||||||
|
@ -20,16 +20,22 @@ import { useLabPagination } from '~/utils/hooks/lab/useLabPagination';
|
||||||
interface IProps {}
|
interface IProps {}
|
||||||
|
|
||||||
const LabLayout: FC<IProps> = () => {
|
const LabLayout: FC<IProps> = () => {
|
||||||
const { is_loading, nodes } = useShallowSelect(selectLabList);
|
const { is_loading, nodes, count } = useShallowSelect(selectLabList);
|
||||||
const dispatch = useDispatch();
|
const dispatch = useDispatch();
|
||||||
|
|
||||||
useLabPagination({ isLoading: is_loading });
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
dispatch(labGetList());
|
dispatch(labGetList());
|
||||||
dispatch(labGetStats());
|
dispatch(labGetStats());
|
||||||
}, [dispatch]);
|
}, [dispatch]);
|
||||||
|
|
||||||
|
const onLoadMore = useCallback(() => {
|
||||||
|
if (nodes.length >= count) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
dispatch(labGetMore());
|
||||||
|
}, [nodes, count]);
|
||||||
|
|
||||||
const isInitialLoading = is_loading && !nodes.length;
|
const isInitialLoading = is_loading && !nodes.length;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
@ -41,7 +47,7 @@ const LabLayout: FC<IProps> = () => {
|
||||||
<LabHead isLoading={isInitialLoading} />
|
<LabHead isLoading={isInitialLoading} />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<LabGrid nodes={nodes} isLoading={isInitialLoading} />
|
<LabGrid nodes={nodes} isLoading={isInitialLoading} onLoadMore={onLoadMore} />
|
||||||
</Group>
|
</Group>
|
||||||
|
|
||||||
<div className={styles.panel}>
|
<div className={styles.panel}>
|
||||||
|
|
|
@ -1,21 +1,44 @@
|
||||||
import { useDispatch } from 'react-redux';
|
import { useCallback, useEffect, useMemo } from 'react';
|
||||||
import { useCallback } from 'react';
|
|
||||||
import { useInfiniteLoader } from '~/utils/hooks/useInfiniteLoader';
|
|
||||||
import { labGetMore } from '~/redux/lab/actions';
|
|
||||||
import { useShallowSelect } from '~/utils/hooks/useShallowSelect';
|
|
||||||
import { selectLabList } from '~/redux/lab/selectors';
|
|
||||||
|
|
||||||
export const useLabPagination = ({ isLoading }) => {
|
export const useLabPagination = (
|
||||||
const { nodes, count } = useShallowSelect(selectLabList);
|
isLoading: boolean,
|
||||||
|
columns: Element[],
|
||||||
|
onLoadMore: () => void
|
||||||
|
) => {
|
||||||
|
const loadOnIntersection = useCallback<IntersectionObserverCallback>(
|
||||||
|
entries => {
|
||||||
|
const isVisible = entries.some(entry => entry.intersectionRatio > 0);
|
||||||
|
|
||||||
const dispatch = useDispatch();
|
if (!isVisible) {
|
||||||
const loadMore = useCallback(() => {
|
return;
|
||||||
if (nodes.length >= count) {
|
}
|
||||||
|
|
||||||
|
onLoadMore();
|
||||||
|
},
|
||||||
|
[onLoadMore]
|
||||||
|
);
|
||||||
|
|
||||||
|
const observer = useMemo(
|
||||||
|
() =>
|
||||||
|
new IntersectionObserver(loadOnIntersection, {
|
||||||
|
threshold: [0],
|
||||||
|
}),
|
||||||
|
[loadOnIntersection]
|
||||||
|
);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (isLoading) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
dispatch(labGetMore());
|
const lastItems = Array.from(columns)
|
||||||
}, [nodes, count]);
|
.map(col => col.children.item(col.childNodes.length - 1))
|
||||||
|
.filter(el => el) as Element[];
|
||||||
|
|
||||||
useInfiniteLoader(loadMore, isLoading);
|
lastItems.forEach(item => observer.observe(item));
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
lastItems.forEach(item => observer.unobserve(item));
|
||||||
|
};
|
||||||
|
}, [observer, columns]);
|
||||||
};
|
};
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue