From a5e2aaabd6bcfcb2df688c4328685610bc426ca1 Mon Sep 17 00:00:00 2001 From: Mirlan Date: Wed, 7 Oct 2020 17:05:28 +0600 Subject: [PATCH] Ott 464 infinite scroll in tournaments filter (#166) * fix(#464): lazy ref problem * refactor(#464): used InfiniteScroll component in tournaments filter list * Update src/features/InfiniteScroll/index.tsx Co-authored-by: Andrey Razdorskiy Co-authored-by: Andrey Razdorskiy --- .../components/TournamentFilter/hooks.tsx | 36 ++++----- .../components/TournamentFilter/index.tsx | 8 +- .../components/TournamentFilter/styled.tsx | 3 +- .../components/TournamentList/index.tsx | 75 +++++++++---------- .../HeaderFilters/store/hooks/index.tsx | 13 ++-- src/features/InfiniteScroll/hooks.tsx | 10 +-- src/features/InfiniteScroll/index.tsx | 13 +++- src/features/Matches/index.tsx | 2 +- src/requests/getSportTournaments.tsx | 10 ++- 9 files changed, 88 insertions(+), 82 deletions(-) diff --git a/src/features/HeaderFilters/components/TournamentFilter/hooks.tsx b/src/features/HeaderFilters/components/TournamentFilter/hooks.tsx index 3063835f..42c210f9 100644 --- a/src/features/HeaderFilters/components/TournamentFilter/hooks.tsx +++ b/src/features/HeaderFilters/components/TournamentFilter/hooks.tsx @@ -1,10 +1,11 @@ -import type { BaseSyntheticEvent, MouseEvent } from 'react' +import type { MouseEvent } from 'react' import { useEffect, useState } from 'react' import find from 'lodash/find' + import type { Tournaments } from 'requests' import { getSportTournaments } from 'requests' -import { useToggle } from 'hooks' +import { useRequest, useToggle } from 'hooks' import { useHeaderFiltersStore } from 'features/HeaderFilters' import { useLexicsStore } from 'features/LexicsStore' @@ -26,25 +27,26 @@ export const useTournamentFilter = () => { isOpen, open, } = useToggle() + const { + isFetching, + request: requestTournaments, + } = useRequest(getSportTournaments) + const [page, setPage] = useState(0) useEffect(() => { setList([]) - getSportTournaments(selectedSportTypeId).then(setList) - }, [selectedSportTypeId]) - - const [requestSent, setRequestSent] = useState(false) + requestTournaments(selectedSportTypeId, 0).then((tournaments) => { + setList(tournaments) + setPage(1) + }) + }, [selectedSportTypeId, requestTournaments]) - const handleScroll = (e: BaseSyntheticEvent) => { - const scrollPositionBottom = e.target.scrollHeight - e.target.scrollTop - - e.target.clientHeight - 400 <= 0 + const fetchMore = async () => { + if (isFetching) return - if (scrollPositionBottom && !requestSent) { - setRequestSent(true) - getSportTournaments(selectedSportTypeId).then((res) => { - setList([...list, ...res]) - setRequestSent(false) - }) - } + const newTournaments = await requestTournaments(selectedSportTypeId, page) + setList([...list, ...newTournaments]) + setPage(page + 1) } const tournaments = normalizeTournaments(list, suffix) @@ -66,7 +68,7 @@ export const useTournamentFilter = () => { return { close, - handleScroll, + fetchMore, isOpen, onResetSelectedTournament, onTournamentSelect, diff --git a/src/features/HeaderFilters/components/TournamentFilter/index.tsx b/src/features/HeaderFilters/components/TournamentFilter/index.tsx index 3cc9b758..f017cb04 100644 --- a/src/features/HeaderFilters/components/TournamentFilter/index.tsx +++ b/src/features/HeaderFilters/components/TournamentFilter/index.tsx @@ -7,7 +7,7 @@ import { TournamentList } from '../TournamentList' import { useTournamentFilter } from './hooks' import { Wrapper, - ListWrapper, + InfiniteScroll, DropdownButton, ButtonTitle, Arrows, @@ -17,7 +17,7 @@ import { export const TournamentFilter = () => { const { close, - handleScroll, + fetchMore, isOpen, onResetSelectedTournament, onTournamentSelect, @@ -50,12 +50,12 @@ export const TournamentFilter = () => { { isOpen && ( - + - + ) } diff --git a/src/features/HeaderFilters/components/TournamentFilter/styled.tsx b/src/features/HeaderFilters/components/TournamentFilter/styled.tsx index 54deb467..ab6bd535 100644 --- a/src/features/HeaderFilters/components/TournamentFilter/styled.tsx +++ b/src/features/HeaderFilters/components/TournamentFilter/styled.tsx @@ -2,8 +2,9 @@ import styled from 'styled-components/macro' import { devices } from 'config/devices' import { customScrollbar } from 'features/Common' +import { InfiniteScroll as InfiniteScrollBase } from 'features/InfiniteScroll' -export const ListWrapper = styled.div` +export const InfiniteScroll = styled(InfiniteScrollBase)` position: absolute; left: 0; top: calc(100% + 8px); diff --git a/src/features/HeaderFilters/components/TournamentList/index.tsx b/src/features/HeaderFilters/components/TournamentList/index.tsx index efc74ba5..55f47147 100644 --- a/src/features/HeaderFilters/components/TournamentList/index.tsx +++ b/src/features/HeaderFilters/components/TournamentList/index.tsx @@ -1,10 +1,9 @@ -import React from 'react' +import React, { SyntheticEvent } from 'react' import map from 'lodash/map' import { SportTypes } from 'config' -import { useItemsList } from 'features/ItemsList/hooks' import { Logo, LogoWrapper, @@ -31,41 +30,39 @@ type TournamentListProps = { tournaments: Array, } -export const TournamentList = ({ onSelect, tournaments }: TournamentListProps) => { - const { - onError, - ref, - } = useItemsList() - - return ( - - {map(tournaments, ({ - country, - fallbackImage, - id, - logo, - name, - sportType, - }) => ( - onSelect(id)} - role='option' - > - - - - - {name} - - {country} - - - ))} - - ) +const onError = (fallbackSrc: string) => (e: SyntheticEvent) => { + // eslint-disable-next-line no-param-reassign + e.currentTarget.src = fallbackSrc } + +export const TournamentList = ({ onSelect, tournaments }: TournamentListProps) => ( + + {map(tournaments, ({ + country, + fallbackImage, + id, + logo, + name, + sportType, + }) => ( + onSelect(id)} + role='option' + > + + + + + {name} + + {country} + + + ))} + +) diff --git a/src/features/HeaderFilters/store/hooks/index.tsx b/src/features/HeaderFilters/store/hooks/index.tsx index 9de28cc6..e51b58e9 100644 --- a/src/features/HeaderFilters/store/hooks/index.tsx +++ b/src/features/HeaderFilters/store/hooks/index.tsx @@ -1,6 +1,9 @@ -import { useMemo, useCallback } from 'react' +import { + useMemo, + useState, + useCallback, +} from 'react' -import isNumber from 'lodash/isNumber' import format from 'date-fns/format' import startOfDay from 'date-fns/startOfDay' @@ -60,11 +63,7 @@ export const useFilters = () => { const [ selectedTournamentId, setSelectedTournamentId, - ] = useQueryParamStore({ - defaultValue: null, - key: filterKeys.TOURNAMENT_ID, - validator: isNumber, - }) + ] = useState(null) const store = useMemo(() => ({ selectedDate, diff --git a/src/features/InfiniteScroll/hooks.tsx b/src/features/InfiniteScroll/hooks.tsx index e83dace8..e1dfa356 100644 --- a/src/features/InfiniteScroll/hooks.tsx +++ b/src/features/InfiniteScroll/hooks.tsx @@ -19,14 +19,14 @@ export const useIntersectionObserver = ({ onIntersect, options }: Args) => { const callbackRef = useRef(onIntersect) const rootRef = useRef(null) const targetRef = useRef(null) - const rootElement = rootRef.current - const targetElement = targetRef.current useEffect(() => { callbackRef.current = onIntersect }, [onIntersect]) useEffect(() => { + const rootElement = rootRef.current + const targetElement = targetRef.current if (!targetElement) { return undefined } @@ -44,11 +44,7 @@ export const useIntersectionObserver = ({ onIntersect, options }: Args) => { return () => { observer.disconnect() } - }, [ - rootElement, - targetElement, - options, - ]) + }, [options]) return { rootRef, targetRef } } diff --git a/src/features/InfiniteScroll/index.tsx b/src/features/InfiniteScroll/index.tsx index 929fc78c..3f2cc50f 100644 --- a/src/features/InfiniteScroll/index.tsx +++ b/src/features/InfiniteScroll/index.tsx @@ -6,22 +6,31 @@ import { Root, Target } from './styled' type Props = { children: ReactNode, + className?: string, + fullPageScroll?: boolean, onFetchMore: () => void, options?: IntersectionObserverInit, } export const InfiniteScroll = ({ children, + className, + fullPageScroll, onFetchMore, options, }: Props) => { - const { targetRef } = useIntersectionObserver({ + const { rootRef, targetRef } = useIntersectionObserver({ onIntersect: onFetchMore, options, }) return ( - + {children} diff --git a/src/features/Matches/index.tsx b/src/features/Matches/index.tsx index 80b6340e..14cc2a5f 100644 --- a/src/features/Matches/index.tsx +++ b/src/features/Matches/index.tsx @@ -44,7 +44,7 @@ export const Matches = (props: Props) => { } return ( - + +const LIMIT = 20 + export const getSportTournaments = ( - sportId?: number | null, - offset: number = 0, + sportId: SportTypes | null, + page: number = 0, ): Promise => { const config = { body: { params: { - _p_limit: 20, - _p_offset: offset, + _p_limit: LIMIT, + _p_offset: page * LIMIT, _p_sport: sportId, }, proc,