mirror of
https://github.com/muerwre/vault-frontend.git
synced 2025-04-24 20:36:40 +07:00
using SWR in tag list
This commit is contained in:
parent
11a9aff8b6
commit
11b39b8766
4 changed files with 57 additions and 46 deletions
|
@ -9,6 +9,7 @@ interface IProps extends HTMLAttributes<HTMLDivElement> {
|
|||
|
||||
const InfiniteScroll: FC<IProps> = ({ children, hasMore, scrollReactPx, loadMore, ...props }) => {
|
||||
const ref = useRef<HTMLDivElement>(null);
|
||||
|
||||
const onScrollEnd = useCallback(
|
||||
(entries: IntersectionObserverEntry[]) => {
|
||||
if (!hasMore || !entries[0].isIntersecting) return;
|
||||
|
|
|
@ -1,29 +1,16 @@
|
|||
import React, { FC, useCallback, useEffect, useMemo } from 'react';
|
||||
import React, { useCallback, useMemo, VFC } from 'react';
|
||||
import { SidebarWrapper } from '~/containers/sidebars/SidebarWrapper';
|
||||
import styles from './styles.module.scss';
|
||||
import { useHistory, useRouteMatch } from 'react-router';
|
||||
import { Icon } from '~/components/input/Icon';
|
||||
import { Link } from 'react-router-dom';
|
||||
import { TagSidebarList } from '~/components/sidebar/TagSidebarList';
|
||||
import { connect } from 'react-redux';
|
||||
import { selectTagNodes } from '~/redux/tag/selectors';
|
||||
import * as ACTIONS from '~/redux/tag/actions';
|
||||
import { LoaderCircle } from '~/components/input/LoaderCircle';
|
||||
import { InfiniteScroll } from '~/components/containers/InfiniteScroll';
|
||||
import { Tag } from '~/components/tags/Tag';
|
||||
import { useTagNodes } from '~/hooks/tag/useTagNodes';
|
||||
|
||||
const mapStateToProps = state => ({
|
||||
nodes: selectTagNodes(state),
|
||||
});
|
||||
|
||||
const mapDispatchToProps = {
|
||||
tagLoadNodes: ACTIONS.tagLoadNodes,
|
||||
tagSetNodes: ACTIONS.tagSetNodes,
|
||||
};
|
||||
|
||||
type Props = ReturnType<typeof mapStateToProps> & typeof mapDispatchToProps & {};
|
||||
|
||||
const TagSidebarUnconnected: FC<Props> = ({ nodes, tagLoadNodes, tagSetNodes }) => {
|
||||
const TagSidebar: VFC = () => {
|
||||
const {
|
||||
params: { tag },
|
||||
url,
|
||||
|
@ -32,41 +19,19 @@ const TagSidebarUnconnected: FC<Props> = ({ nodes, tagLoadNodes, tagSetNodes })
|
|||
|
||||
const basePath = url.replace(new RegExp(`\/tag\/${tag}$`), '');
|
||||
const onClose = useCallback(() => history.push(basePath), [basePath, history]);
|
||||
|
||||
useEffect(() => {
|
||||
tagLoadNodes(tag);
|
||||
|
||||
return () => {
|
||||
tagSetNodes({ list: [], count: 0 });
|
||||
};
|
||||
}, [tag, tagLoadNodes, tagSetNodes]);
|
||||
|
||||
const loadMore = useCallback(() => {
|
||||
if (nodes.isLoading) return;
|
||||
tagLoadNodes(tag);
|
||||
}, [tagLoadNodes, tag, nodes.isLoading]);
|
||||
|
||||
const { nodes, hasMore, isLoading, loadMore } = useTagNodes(tag);
|
||||
const title = useMemo(() => decodeURIComponent(tag), [tag]);
|
||||
const progress = nodes.count > 0 ? `${(nodes.list.length / nodes.count) * 100}%` : '0';
|
||||
|
||||
const hasMore = nodes.count > nodes.list.length;
|
||||
|
||||
return (
|
||||
<SidebarWrapper onClose={onClose}>
|
||||
<div className={styles.wrap}>
|
||||
<div className={styles.content}>
|
||||
<div className={styles.head}>
|
||||
{nodes.count > 0 && (
|
||||
<div className={styles.progress}>
|
||||
<div className={styles.bar} style={{ width: progress }} />
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div className={styles.tag}>
|
||||
<Tag tag={{ title }} size="big" />
|
||||
</div>
|
||||
|
||||
{nodes.isLoading && (
|
||||
{isLoading && (
|
||||
<div className={styles.sync}>
|
||||
<LoaderCircle size={20} />
|
||||
</div>
|
||||
|
@ -79,7 +44,7 @@ const TagSidebarUnconnected: FC<Props> = ({ nodes, tagLoadNodes, tagSetNodes })
|
|||
</div>
|
||||
</div>
|
||||
|
||||
{!nodes.count && !nodes.isLoading ? (
|
||||
{!nodes.length && !isLoading ? (
|
||||
<div className={styles.none}>
|
||||
<Icon icon="sad" size={120} />
|
||||
<div>
|
||||
|
@ -91,7 +56,7 @@ const TagSidebarUnconnected: FC<Props> = ({ nodes, tagLoadNodes, tagSetNodes })
|
|||
</div>
|
||||
) : (
|
||||
<InfiniteScroll hasMore={hasMore} loadMore={loadMore} className={styles.list}>
|
||||
<TagSidebarList nodes={nodes.list} />
|
||||
<TagSidebarList nodes={nodes} />
|
||||
</InfiniteScroll>
|
||||
)}
|
||||
</div>
|
||||
|
@ -100,6 +65,4 @@ const TagSidebarUnconnected: FC<Props> = ({ nodes, tagLoadNodes, tagSetNodes })
|
|||
);
|
||||
};
|
||||
|
||||
const TagSidebar = connect(mapStateToProps, mapDispatchToProps)(TagSidebarUnconnected);
|
||||
|
||||
export { TagSidebar };
|
||||
|
|
|
@ -43,8 +43,7 @@ export const useGetComments = (nodeId: number) => {
|
|||
);
|
||||
|
||||
const comments = flatten(data || []);
|
||||
const hasMore =
|
||||
!!data?.[data?.length - 1].length && data[data.length - 1].length === COMMENTS_DISPLAY;
|
||||
const hasMore = (data?.[size - 1]?.length || 0) >= COMMENTS_DISPLAY;
|
||||
|
||||
const onLoadMoreComments = useCallback(() => setSize(size + 1), [setSize, size]);
|
||||
|
||||
|
|
48
src/hooks/tag/useTagNodes.ts
Normal file
48
src/hooks/tag/useTagNodes.ts
Normal file
|
@ -0,0 +1,48 @@
|
|||
import { KeyLoader } from 'swr';
|
||||
import { INode } from '~/redux/types';
|
||||
import { API } from '~/constants/api';
|
||||
import { flatten, isNil } from 'ramda';
|
||||
import useSWRInfinite from 'swr/infinite';
|
||||
import { useCallback } from 'react';
|
||||
import { apiGetNodesOfTag } from '~/redux/tag/api';
|
||||
import { COMMENTS_DISPLAY } from '~/constants/node';
|
||||
|
||||
const PAGE_SIZE = 10;
|
||||
|
||||
const getKey: (tag: string) => KeyLoader<INode[]> = tag => (pageIndex, previousPageData) => {
|
||||
if (pageIndex > 0 && !previousPageData?.length) return null;
|
||||
return `${API.TAG.NODES}?tag=${tag}&page=${pageIndex}`;
|
||||
};
|
||||
|
||||
const extractKey = (key: string) => {
|
||||
const re = new RegExp(`${API.TAG.NODES}\\?tag=[^&]+&page=(\\d+)`);
|
||||
const match = key.match(re);
|
||||
|
||||
if (!match || !Array.isArray(match) || isNil(match[1])) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return parseInt(match[1], 10) || 0;
|
||||
};
|
||||
|
||||
export const useTagNodes = (tag: string) => {
|
||||
const { data, isValidating, setSize, size, mutate } = useSWRInfinite(
|
||||
getKey(tag),
|
||||
async (key: string) => {
|
||||
const result = await apiGetNodesOfTag({
|
||||
tag,
|
||||
limit: PAGE_SIZE,
|
||||
offset: extractKey(key) * PAGE_SIZE,
|
||||
});
|
||||
|
||||
return result.nodes;
|
||||
}
|
||||
);
|
||||
|
||||
const nodes = flatten(data || []);
|
||||
const hasMore = (data?.[size - 1]?.length || 0) >= PAGE_SIZE;
|
||||
|
||||
const loadMore = useCallback(() => setSize(size + 1), [setSize, size]);
|
||||
|
||||
return { nodes, hasMore, loadMore, isLoading: !data && isValidating, mutate, data };
|
||||
};
|
Loading…
Add table
Add a link
Reference in a new issue