From 3d402de104dfd4a49cc173d9644d054746cf2dee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E1=84=8C=E1=85=A5=E1=86=BC=E1=84=83=E1=85=A2=E1=84=8B?= =?UTF-8?q?=E1=85=B2=E1=86=AB?= Date: Sun, 5 Sep 2021 05:54:38 +0900 Subject: [PATCH 1/9] =?UTF-8?q?feat:=20ADD=201=EC=B0=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/WikiTable/WikiTable.tsx | 27 +++++---------- src/components/WikiTable/WikiTableRow.tsx | 29 ++++++++++++++-- src/hooks/useIntersectionObserver.ts | 42 +++++++++++++++++++++++ 3 files changed, 76 insertions(+), 22 deletions(-) create mode 100644 src/hooks/useIntersectionObserver.ts diff --git a/src/components/WikiTable/WikiTable.tsx b/src/components/WikiTable/WikiTable.tsx index 4e38d02c..d31f3ea9 100644 --- a/src/components/WikiTable/WikiTable.tsx +++ b/src/components/WikiTable/WikiTable.tsx @@ -1,25 +1,14 @@ -import React, { useEffect } from 'react'; +import React, { useCallback, useEffect, useRef } from 'react'; +import { WikiWord } from 'types'; import WikiTableRow from './WikiTableRow'; -import usePagination from '../../hooks/usePagination'; - -export default function WikiTable({ words = [] }: { words: string[] }) { - const { onPrevious, onNext, currentPage, result, isLastPage, isFirstPage } = - usePagination({ - source: words, - offset: 2, - }); - +export default function WikiTable({ words = [] }: { words: WikiWord[] }) { + const getIsLast = (index): boolean => { + return words.length - 1 == index; + }; return ( <> - {currentPage} - - @@ -28,8 +17,8 @@ export default function WikiTable({ words = [] }: { words: string[] }) { - {result.map(word => ( - + {words.map((word, index) => ( + ))}
diff --git a/src/components/WikiTable/WikiTableRow.tsx b/src/components/WikiTable/WikiTableRow.tsx index 13e8be63..788e4302 100644 --- a/src/components/WikiTable/WikiTableRow.tsx +++ b/src/components/WikiTable/WikiTableRow.tsx @@ -1,9 +1,32 @@ -import React from 'react'; +import { lstat } from 'fs'; +import useIntersectionObserver from 'hooks/useIntersectionObserver'; +import React, { RefObject, useEffect, useRef } from 'react'; import { WikiWord } from '../../types'; +type wikiTableRowProps = { + description: string; + name: string; + last: boolean; +}; +export default function WikiTableRow({ + name, + description, + last, +}: wikiTableRowProps) { + const ref = useRef(null); + const entry = useIntersectionObserver(ref, {}); + const isVisible = () => { + return entry?.isIntersecting; + }; -export default function WikiTableRow({ name, description }: WikiWord) { - return ( + return last ? ( + + {name} + + {description} {last.toString()} {isVisible()} + + + ) : ( {name} {description} diff --git a/src/hooks/useIntersectionObserver.ts b/src/hooks/useIntersectionObserver.ts new file mode 100644 index 00000000..30c77403 --- /dev/null +++ b/src/hooks/useIntersectionObserver.ts @@ -0,0 +1,42 @@ +import { RefObject, useEffect, useState } from 'react'; + +interface Args extends IntersectionObserverInit { + freezeOnceVisible?: boolean; +} + +function useIntersectionObserver( + elementRef: RefObject, + { + threshold = 0, + root = null, + rootMargin = '0%', + freezeOnceVisible = false, + }: Args, +): IntersectionObserverEntry | undefined { + const [entry, setEntry] = useState(); + + const frozen = entry?.isIntersecting && freezeOnceVisible; + + const updateEntry = ([entry]: IntersectionObserverEntry[]): void => { + setEntry(entry); + }; + + useEffect(() => { + const node = elementRef?.current; // DOM Ref + const hasIOSupport = !!window.IntersectionObserver; + + if (!hasIOSupport || frozen || !node) return; + + const observerParams = { threshold, root, rootMargin }; + const observer = new IntersectionObserver(updateEntry, observerParams); + + observer.observe(node); + return () => observer.disconnect(); + + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [elementRef, threshold, root, rootMargin, frozen]); + + return entry; +} + +export default useIntersectionObserver; From 447aeeaca0bc86693fdbfb0a454c998f6618f077 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E1=84=8C=E1=85=A5=E1=86=BC=E1=84=83=E1=85=A2=E1=84=8B?= =?UTF-8?q?=E1=85=B2=E1=86=AB?= Date: Sat, 11 Sep 2021 01:57:00 +0900 Subject: [PATCH 2/9] feat: Add infinite scroll hooks and Apply it in Wiki Table --- src/components/WikiTable/WikiTable.tsx | 18 +++++++++--- src/components/WikiTable/WikiTableRow.tsx | 30 ++++++++------------ src/hooks/useInfiniteScroll.ts | 34 +++++++++++++++++++++++ src/hooks/useIntersectionObserver.ts | 2 -- 4 files changed, 60 insertions(+), 24 deletions(-) create mode 100644 src/hooks/useInfiniteScroll.ts diff --git a/src/components/WikiTable/WikiTable.tsx b/src/components/WikiTable/WikiTable.tsx index d31f3ea9..0c6a0571 100644 --- a/src/components/WikiTable/WikiTable.tsx +++ b/src/components/WikiTable/WikiTable.tsx @@ -2,11 +2,16 @@ import React, { useCallback, useEffect, useRef } from 'react'; import { WikiWord } from 'types'; import WikiTableRow from './WikiTableRow'; +import useInfiniteScroll from '../../hooks/useInfiniteScroll'; export default function WikiTable({ words = [] }: { words: WikiWord[] }) { - const getIsLast = (index): boolean => { - return words.length - 1 == index; + const { result, onNext } = useInfiniteScroll({ + source: words, + }); + const getLastRow = (index): boolean => { + return result.length - 1 == index; }; + return ( <> @@ -17,8 +22,13 @@ export default function WikiTable({ words = [] }: { words: WikiWord[] }) { - {words.map((word, index) => ( - + {result.map((word, index) => ( + ))}
diff --git a/src/components/WikiTable/WikiTableRow.tsx b/src/components/WikiTable/WikiTableRow.tsx index 788e4302..530d2071 100644 --- a/src/components/WikiTable/WikiTableRow.tsx +++ b/src/components/WikiTable/WikiTableRow.tsx @@ -1,35 +1,29 @@ -import { lstat } from 'fs'; import useIntersectionObserver from 'hooks/useIntersectionObserver'; -import React, { RefObject, useEffect, useRef } from 'react'; +import React, { useEffect, useRef } from 'react'; -import { WikiWord } from '../../types'; type wikiTableRowProps = { description: string; name: string; last: boolean; + onNext: () => void; }; export default function WikiTableRow({ name, description, last, + onNext, }: wikiTableRowProps) { const ref = useRef(null); - const entry = useIntersectionObserver(ref, {}); - const isVisible = () => { - return entry?.isIntersecting; - }; - - return last ? ( - - {name} - - {description} {last.toString()} {isVisible()} - - - ) : ( - + const entry = useIntersectionObserver(ref, { freezeOnceVisible: true }); + useEffect(() => { + if (entry?.intersectionRatio) onNext(); + }, [entry]); + const content = ( + <> {name} {description} - + ); + + return last ? {content} : {content}; } diff --git a/src/hooks/useInfiniteScroll.ts b/src/hooks/useInfiniteScroll.ts new file mode 100644 index 00000000..9587be78 --- /dev/null +++ b/src/hooks/useInfiniteScroll.ts @@ -0,0 +1,34 @@ +import { useEffect, useState } from 'react'; + +const usePagination = ({ source = [], limit = 10, offset = 0 }) => { + const getResult = () => { + const startIdx = 0; + const endIdx = currentOffest; + return source.slice(startIdx, endIdx); + }; + + const getMaxOffset = () => { + return source.length; + }; + + const [currentOffest, setCurrentOffest] = useState(limit); + + useEffect(() => { + if (currentOffest !== limit) setCurrentOffest(limit); + else getResult(); + }, [source]); + + const onNext = () => { + if (currentOffest >= getMaxOffset()) return; + setCurrentOffest(currentOffest + limit); + }; + + return { + result: getResult(), + currentOffest, + isLastOffset: currentOffest > getMaxOffset(), + onNext, + }; +}; + +export default usePagination; diff --git a/src/hooks/useIntersectionObserver.ts b/src/hooks/useIntersectionObserver.ts index 30c77403..f4488359 100644 --- a/src/hooks/useIntersectionObserver.ts +++ b/src/hooks/useIntersectionObserver.ts @@ -16,7 +16,6 @@ function useIntersectionObserver( const [entry, setEntry] = useState(); const frozen = entry?.isIntersecting && freezeOnceVisible; - const updateEntry = ([entry]: IntersectionObserverEntry[]): void => { setEntry(entry); }; @@ -24,7 +23,6 @@ function useIntersectionObserver( useEffect(() => { const node = elementRef?.current; // DOM Ref const hasIOSupport = !!window.IntersectionObserver; - if (!hasIOSupport || frozen || !node) return; const observerParams = { threshold, root, rootMargin }; From 7e0aac72157c7250c1ec80e1f1af94c17f9ef8e8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E1=84=8C=E1=85=A5=E1=86=BC=E1=84=83=E1=85=A2=E1=84=8B?= =?UTF-8?q?=E1=85=B2=E1=86=AB?= Date: Sun, 19 Sep 2021 01:33:28 +0900 Subject: [PATCH 3/9] fix: change intersectionRatio to isIntersecting for accurate use of the IntersectionObserver. --- src/components/WikiTable/WikiTableRow.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/WikiTable/WikiTableRow.tsx b/src/components/WikiTable/WikiTableRow.tsx index 530d2071..7a2a5e4b 100644 --- a/src/components/WikiTable/WikiTableRow.tsx +++ b/src/components/WikiTable/WikiTableRow.tsx @@ -16,7 +16,7 @@ export default function WikiTableRow({ const ref = useRef(null); const entry = useIntersectionObserver(ref, { freezeOnceVisible: true }); useEffect(() => { - if (entry?.intersectionRatio) onNext(); + if (entry?.isIntersecting) onNext(); }, [entry]); const content = ( <> From eaeae1adf6ea5431cde8765dbf113e2447e924a1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E1=84=8C=E1=85=A5=E1=86=BC=E1=84=83=E1=85=A2=E1=84=8B?= =?UTF-8?q?=E1=85=B2=E1=86=AB?= Date: Sun, 19 Sep 2021 01:46:56 +0900 Subject: [PATCH 4/9] refactor: When using a IntersectionObserver, ref is required. Therefore, useIntersectionObserver was changed to return ref --- src/components/WikiTable/WikiTableRow.tsx | 5 +++-- src/hooks/useIntersectionObserver.ts | 27 ++++++++++++----------- 2 files changed, 17 insertions(+), 15 deletions(-) diff --git a/src/components/WikiTable/WikiTableRow.tsx b/src/components/WikiTable/WikiTableRow.tsx index 7a2a5e4b..b79119ae 100644 --- a/src/components/WikiTable/WikiTableRow.tsx +++ b/src/components/WikiTable/WikiTableRow.tsx @@ -13,11 +13,12 @@ export default function WikiTableRow({ last, onNext, }: wikiTableRowProps) { - const ref = useRef(null); - const entry = useIntersectionObserver(ref, { freezeOnceVisible: true }); + const { ref, entry } = useIntersectionObserver({ freezeOnceVisible: true }); + useEffect(() => { if (entry?.isIntersecting) onNext(); }, [entry]); + const content = ( <> {name} diff --git a/src/hooks/useIntersectionObserver.ts b/src/hooks/useIntersectionObserver.ts index f4488359..0344e2bf 100644 --- a/src/hooks/useIntersectionObserver.ts +++ b/src/hooks/useIntersectionObserver.ts @@ -1,18 +1,19 @@ -import { RefObject, useEffect, useState } from 'react'; +import { RefObject, useEffect, useRef, useState } from 'react'; interface Args extends IntersectionObserverInit { freezeOnceVisible?: boolean; } -function useIntersectionObserver( - elementRef: RefObject, - { - threshold = 0, - root = null, - rootMargin = '0%', - freezeOnceVisible = false, - }: Args, -): IntersectionObserverEntry | undefined { +function useIntersectionObserver({ + threshold = 0, + root = null, + rootMargin = '0%', + freezeOnceVisible = false, +}: Args): { + ref: RefObject | null; + entry: IntersectionObserverEntry | undefined; +} { + const ref = useRef(null); const [entry, setEntry] = useState(); const frozen = entry?.isIntersecting && freezeOnceVisible; @@ -21,7 +22,7 @@ function useIntersectionObserver( }; useEffect(() => { - const node = elementRef?.current; // DOM Ref + const node = ref?.current; // DOM Ref const hasIOSupport = !!window.IntersectionObserver; if (!hasIOSupport || frozen || !node) return; @@ -32,9 +33,9 @@ function useIntersectionObserver( return () => observer.disconnect(); // eslint-disable-next-line react-hooks/exhaustive-deps - }, [elementRef, threshold, root, rootMargin, frozen]); + }, [ref, threshold, root, rootMargin, frozen]); - return entry; + return { ref, entry }; } export default useIntersectionObserver; From c8c7df1abba428b3be467d942d165489e7642f90 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E1=84=8C=E1=85=A5=E1=86=BC=E1=84=83=E1=85=A2=E1=84=8B?= =?UTF-8?q?=E1=85=B2=E1=86=AB?= Date: Sun, 19 Sep 2021 04:02:13 +0900 Subject: [PATCH 5/9] enhance: Add package for Adding the polyfill about IntersectionObserver --- package.json | 1 + src/hooks/useIntersectionObserver.ts | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/package.json b/package.json index f2111902..d7b9aa58 100644 --- a/package.json +++ b/package.json @@ -27,6 +27,7 @@ "clsx": "^1.1.1", "facepaint": "^1.2.1", "file-loader": "^6.2.0", + "intersection-observer": "^0.12.0", "prism-react-renderer": "^1.2.1", "react": "^17.0.1", "react-dom": "^17.0.1", diff --git a/src/hooks/useIntersectionObserver.ts b/src/hooks/useIntersectionObserver.ts index 0344e2bf..df85dc0c 100644 --- a/src/hooks/useIntersectionObserver.ts +++ b/src/hooks/useIntersectionObserver.ts @@ -1,5 +1,5 @@ import { RefObject, useEffect, useRef, useState } from 'react'; - +import 'intersection-observer'; interface Args extends IntersectionObserverInit { freezeOnceVisible?: boolean; } From 1efccada0a6cb05b87353c6eb9df1abf9061abc5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E1=84=8C=E1=85=A5=E1=86=BC=E1=84=83=E1=85=A2=E1=84=8B?= =?UTF-8?q?=E1=85=B2=E1=86=AB?= Date: Wed, 29 Sep 2021 04:47:51 +0900 Subject: [PATCH 6/9] fix: Remove unused code. --- src/components/WikiTable/WikiTableRow.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/WikiTable/WikiTableRow.tsx b/src/components/WikiTable/WikiTableRow.tsx index b79119ae..6026cc90 100644 --- a/src/components/WikiTable/WikiTableRow.tsx +++ b/src/components/WikiTable/WikiTableRow.tsx @@ -1,5 +1,5 @@ import useIntersectionObserver from 'hooks/useIntersectionObserver'; -import React, { useEffect, useRef } from 'react'; +import React, { useEffect } from 'react'; type wikiTableRowProps = { description: string; From a6fc0282a7ed5853ff3d5329b68e937a14a88895 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E1=84=8C=E1=85=A5=E1=86=BC=E1=84=83=E1=85=A2=E1=84=8B?= =?UTF-8?q?=E1=85=B2=E1=86=AB?= Date: Wed, 29 Sep 2021 05:06:38 +0900 Subject: [PATCH 7/9] review: Add delay option used in callback action when target on screen --- src/hooks/useIntersectionObserver.ts | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/hooks/useIntersectionObserver.ts b/src/hooks/useIntersectionObserver.ts index df85dc0c..0dc83cd9 100644 --- a/src/hooks/useIntersectionObserver.ts +++ b/src/hooks/useIntersectionObserver.ts @@ -2,6 +2,7 @@ import { RefObject, useEffect, useRef, useState } from 'react'; import 'intersection-observer'; interface Args extends IntersectionObserverInit { freezeOnceVisible?: boolean; + delay?: number; } function useIntersectionObserver({ @@ -9,6 +10,7 @@ function useIntersectionObserver({ root = null, rootMargin = '0%', freezeOnceVisible = false, + delay = 0, }: Args): { ref: RefObject | null; entry: IntersectionObserverEntry | undefined; @@ -18,7 +20,9 @@ function useIntersectionObserver({ const frozen = entry?.isIntersecting && freezeOnceVisible; const updateEntry = ([entry]: IntersectionObserverEntry[]): void => { - setEntry(entry); + setTimeout(() => { + setEntry(entry); + }, delay); }; useEffect(() => { From 9ef2ba88831d7e3ba3be81a52eaa9fa337a30569 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E1=84=8C=E1=85=A5=E1=86=BC=E1=84=83=E1=85=A2=E1=84=8B?= =?UTF-8?q?=E1=85=B2=E1=86=AB?= Date: Sun, 17 Oct 2021 04:55:33 +0900 Subject: [PATCH 8/9] review: remove unnecessary code by importing polyfills --- src/hooks/useIntersectionObserver.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/hooks/useIntersectionObserver.ts b/src/hooks/useIntersectionObserver.ts index 0dc83cd9..defee57a 100644 --- a/src/hooks/useIntersectionObserver.ts +++ b/src/hooks/useIntersectionObserver.ts @@ -27,8 +27,7 @@ function useIntersectionObserver({ useEffect(() => { const node = ref?.current; // DOM Ref - const hasIOSupport = !!window.IntersectionObserver; - if (!hasIOSupport || frozen || !node) return; + if (frozen || !node) return; const observerParams = { threshold, root, rootMargin }; const observer = new IntersectionObserver(updateEntry, observerParams); From 2ad808349ade307e1a736329e828cbc1e0ea2ee1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E1=84=8C=E1=85=A5=E1=86=BC=E1=84=83=E1=85=A2=E1=84=8B?= =?UTF-8?q?=E1=85=B2=E1=86=AB?= Date: Sun, 17 Oct 2021 05:09:52 +0900 Subject: [PATCH 9/9] review: Add types --- src/hooks/useInfiniteScroll.ts | 27 ++++++++++++++++++++++----- src/hooks/useIntersectionObserver.ts | 2 +- 2 files changed, 23 insertions(+), 6 deletions(-) diff --git a/src/hooks/useInfiniteScroll.ts b/src/hooks/useInfiniteScroll.ts index 9587be78..73b98993 100644 --- a/src/hooks/useInfiniteScroll.ts +++ b/src/hooks/useInfiniteScroll.ts @@ -1,17 +1,34 @@ import { useEffect, useState } from 'react'; +import { WikiWord } from 'types'; -const usePagination = ({ source = [], limit = 10, offset = 0 }) => { - const getResult = () => { +type Args = { + source: WikiWord[]; + limit: number; + offset: number; +}; + +type Returns = { + result: WikiWord[]; + currentOffest: number; + isLastOffset: boolean; + onNext: () => void; +}; +const usePagination = ({ + source = [], + limit = 10, + offset = 0, +}: Args): Returns => { + const getResult = (): WikiWord[] => { const startIdx = 0; - const endIdx = currentOffest; + const endIdx: number = currentOffest; return source.slice(startIdx, endIdx); }; - const getMaxOffset = () => { + const getMaxOffset = (): number => { return source.length; }; - const [currentOffest, setCurrentOffest] = useState(limit); + const [currentOffest, setCurrentOffest] = useState(limit); useEffect(() => { if (currentOffest !== limit) setCurrentOffest(limit); diff --git a/src/hooks/useIntersectionObserver.ts b/src/hooks/useIntersectionObserver.ts index defee57a..4a6ef9c5 100644 --- a/src/hooks/useIntersectionObserver.ts +++ b/src/hooks/useIntersectionObserver.ts @@ -18,7 +18,7 @@ function useIntersectionObserver({ const ref = useRef(null); const [entry, setEntry] = useState(); - const frozen = entry?.isIntersecting && freezeOnceVisible; + const frozen: boolean = entry?.isIntersecting && freezeOnceVisible; const updateEntry = ([entry]: IntersectionObserverEntry[]): void => { setTimeout(() => { setEntry(entry);