From 11b39b8766dfa13c7b61b6d9adc3f96721ee2d90 Mon Sep 17 00:00:00 2001 From: Fedor Katurov Date: Mon, 3 Jan 2022 12:52:40 +0700 Subject: [PATCH] using SWR in tag list --- .../containers/InfiniteScroll/index.tsx | 1 + src/containers/sidebars/TagSidebar/index.tsx | 51 +++---------------- src/hooks/comments/useGetComments.ts | 3 +- src/hooks/tag/useTagNodes.ts | 48 +++++++++++++++++ 4 files changed, 57 insertions(+), 46 deletions(-) create mode 100644 src/hooks/tag/useTagNodes.ts diff --git a/src/components/containers/InfiniteScroll/index.tsx b/src/components/containers/InfiniteScroll/index.tsx index 1fe1ed94..18011736 100644 --- a/src/components/containers/InfiniteScroll/index.tsx +++ b/src/components/containers/InfiniteScroll/index.tsx @@ -9,6 +9,7 @@ interface IProps extends HTMLAttributes { const InfiniteScroll: FC = ({ children, hasMore, scrollReactPx, loadMore, ...props }) => { const ref = useRef(null); + const onScrollEnd = useCallback( (entries: IntersectionObserverEntry[]) => { if (!hasMore || !entries[0].isIntersecting) return; diff --git a/src/containers/sidebars/TagSidebar/index.tsx b/src/containers/sidebars/TagSidebar/index.tsx index 559dee4f..b56ac1bb 100644 --- a/src/containers/sidebars/TagSidebar/index.tsx +++ b/src/containers/sidebars/TagSidebar/index.tsx @@ -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 mapDispatchToProps & {}; - -const TagSidebarUnconnected: FC = ({ nodes, tagLoadNodes, tagSetNodes }) => { +const TagSidebar: VFC = () => { const { params: { tag }, url, @@ -32,41 +19,19 @@ const TagSidebarUnconnected: FC = ({ 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 (
- {nodes.count > 0 && ( -
-
-
- )} -
- {nodes.isLoading && ( + {isLoading && (
@@ -79,7 +44,7 @@ const TagSidebarUnconnected: FC = ({ nodes, tagLoadNodes, tagSetNodes })
- {!nodes.count && !nodes.isLoading ? ( + {!nodes.length && !isLoading ? (
@@ -91,7 +56,7 @@ const TagSidebarUnconnected: FC = ({ nodes, tagLoadNodes, tagSetNodes })
) : ( - + )}
@@ -100,6 +65,4 @@ const TagSidebarUnconnected: FC = ({ nodes, tagLoadNodes, tagSetNodes }) ); }; -const TagSidebar = connect(mapStateToProps, mapDispatchToProps)(TagSidebarUnconnected); - export { TagSidebar }; diff --git a/src/hooks/comments/useGetComments.ts b/src/hooks/comments/useGetComments.ts index 93575a1b..56a7eab3 100644 --- a/src/hooks/comments/useGetComments.ts +++ b/src/hooks/comments/useGetComments.ts @@ -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]); diff --git a/src/hooks/tag/useTagNodes.ts b/src/hooks/tag/useTagNodes.ts new file mode 100644 index 00000000..e97a58f6 --- /dev/null +++ b/src/hooks/tag/useTagNodes.ts @@ -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 = 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 }; +};