From e68a9facc4bcd1d8280a51cd542ff6ccb0d5b06f Mon Sep 17 00:00:00 2001 From: Wayne <75321423+WayneKim92@users.noreply.github.com> Date: Mon, 23 Sep 2024 16:11:47 +0900 Subject: [PATCH] =?UTF-8?q?[feat]=20api=20=ED=98=B8=EC=B6=9C=20=EC=BF=A8?= =?UTF-8?q?=ED=83=80=EC=9E=84=20=EC=B6=94=EA=B0=80=20(#18)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Update README.md * Update README.md * chore: 당겨서 새로고침 작동 확인 * feat: cache도 clear 하기 * feat: api 중복 호출 방지 --- README.md | 8 ++++ example/src/screens/ReanimatedListScreen.tsx | 21 +++++++++- src/index.tsx | 41 ++++++++++++++++---- 3 files changed, 60 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index 49c773b..0504fac 100644 --- a/README.md +++ b/README.md @@ -2,6 +2,14 @@ Do you need to update FlatList data frequently? Are people constantly complaining that they don't see the most up-to-date information? Try using this component by simply entering data. Refreshing is the responsibility of this component, Get away from data refreshing logic and focus on other things. +## Features +- [X] Flatlist based on infinite scroll page. +- [X] Take a page and combine them existen pages. +- [X] When you move to another screen and then come back, only the page you are currently viewing is retrieved and the page in the list is updated. + - If the page you are currently viewing is between the current page and the next page, both pages will be refreshed. +- [X] Reset by external request. +- [X] refresh only the page currently being viewed or a specific page. +- [ ] In preparation) list catching. ## Installation diff --git a/example/src/screens/ReanimatedListScreen.tsx b/example/src/screens/ReanimatedListScreen.tsx index b6a6df4..9281884 100644 --- a/example/src/screens/ReanimatedListScreen.tsx +++ b/example/src/screens/ReanimatedListScreen.tsx @@ -32,7 +32,7 @@ import { Row } from '@wayne-kim/react-native-layout'; export default function ReanimatedListScreen() { const [category, setCategory] = useState('ALL'); - const [size, setSize] = useState(10); + const [size, setSize] = useState(20); const previousSize = useRef(size); const [ownerId, setOwnerId] = useState(29); const previousOwnerId = useRef(ownerId); @@ -45,6 +45,9 @@ export default function ReanimatedListScreen() { const translateY = useSharedValue(0); const [headerHeight, setHeaderHeight] = useState(0); + // 다른 상태값에 의해 한번더 렌더링 되는 케이스 추가 + const [_extraState, setExtraState] = useState(0); + const animatedStyle = useAnimatedStyle(() => { const currentScrollY = offsetY.value; const deltaY = Math.round(currentScrollY - lastScrollYRef.current); @@ -90,13 +93,18 @@ export default function ReanimatedListScreen() { { // If you want to refresh the page to which the item belongs after changing the status of the item. // Example) freshFlatListRef.current?.refreshWatching(index); }} > - LIKE! + LIKE! ); @@ -143,6 +151,11 @@ export default function ReanimatedListScreen() { previousSize.current = size; freshFlatListRef.current?.reset(); } + + setTimeout(() => { + freshFlatListRef.current?.refreshWatching(); + setExtraState((prev) => prev + 1); + }, 100); }, [ownerId, size]); return ( @@ -214,6 +227,10 @@ export default function ReanimatedListScreen() { devMode={true} FlatListComponent={Reanimated.FlatList} onScroll={scrollHandler} + refreshing={false} + onRefresh={() => { + freshFlatListRef.current?.reset(); + }} style={[ { flex: 1, diff --git a/src/index.tsx b/src/index.tsx index 808a1e2..efd13ee 100644 --- a/src/index.tsx +++ b/src/index.tsx @@ -1,3 +1,4 @@ +// 코드가 길어지니 함수형 컴포넌트가 더 같다. 클래스로 바꾸는게 나을까? useEffect의 의존성 배열에 값이 많아지니 문제다... import { type FlatListProps, FlatList, @@ -30,6 +31,7 @@ type onViewableItemsChangedParam = { const FIRST_PAGE = 1; // Type of FreshFlatList +type CacheType = Map; export type FetchType = 'first' | 'watching' | 'end-reached'; export type FetchInputMeta = { fetchType: FetchType; @@ -47,6 +49,7 @@ export interface FreshFlatListProps isFocused?: boolean; fetchList: (fetchInputMeta: FetchInputMeta) => FetchOutputMeta; devMode?: boolean; + fetchCoolTime?: number; FlatListComponent?: | ComponentType> | typeof Animated.FlatList; @@ -61,7 +64,7 @@ export interface FreshFlatListRef { * Refresh the current page of the list. * @param index If the index is given, the page containing the index is refreshed. If not, the current page is refreshed. */ - refreshWatching: (index: number) => void; + refreshWatching: (index?: number) => void; /** * Get the FlatList component. */ @@ -79,6 +82,7 @@ function FreshFlatList( devMode, isFocused, onEndReachedThreshold = 0.5, + fetchCoolTime = 1000, FlatListComponent = FlatList, LoadingComponent, ...otherProps @@ -89,9 +93,7 @@ function FreshFlatList( const [isLoading, setIsLoading] = useState(true); const [data, setData] = useState([]); - const cache = useRef>( - new Map() - ).current; + const cache = useRef>(new Map()).current; const isFetchingFirstPageRef = useRef(false); const isFetchingLastEdgePageRef = useRef(false); const recentlyFetchLastEdgePageRef = useRef(FIRST_PAGE); @@ -146,6 +148,7 @@ function FreshFlatList( watchingPagesRef.current = { first: FIRST_PAGE, second: FIRST_PAGE }; isFirstFetchRef.current = true; stopNextFetchRef.current = false; + cache.clear(); setIsLoading(true); setData([]); devLog('#reset', { @@ -153,10 +156,11 @@ function FreshFlatList( watchingPages: watchingPagesRef.current, isFirstFetch: isFirstFetchRef.current, stopNextFetch: stopNextFetchRef.current, + cache: cache.size, data: data.length, isLoading, }); - }, [data, devLog, isLoading]); + }, [cache, data.length, devLog, isLoading]); const getAllCachedData = useCallback(() => { let allData: T[] = []; @@ -175,17 +179,38 @@ function FreshFlatList( devLog('#fetchAndCache | fetchType:', fetchType); devLog('#fetchAndCache | fetchPage:', page); + // 불필요한 리스트 fetch 방지 + const currentTime = Date.now(); + const cacheEntry = cache.get(page); + if ( + cacheEntry && + currentTime - new Date(cacheEntry.timestamp).getTime() < fetchCoolTime + ) { + devLog( + '#fetchAndCache | 중복 호출 방지 | fetchCoolTime:', + currentTime - new Date(cacheEntry.timestamp).getTime() + ); + return { list: [], isLastPage: false }; + } + cache.set(page, { + data: [], + timestamp: new Date().toISOString(), + }); + + // fetch const { list, isLastPage } = await fetchList({ fetchPage: page, fetchType: fetchType, previousAllData: getAllCachedData(), }); setIsLoading(false); - const timestamp = new Date().toISOString(); - cache.set(page, { data: list, timestamp }); + cache.set(page, { + data: list, + timestamp: new Date().toISOString(), + }); return { list, isLastPage }; }, - [cache, devLog, fetchList, getAllCachedData] + [cache, devLog, fetchCoolTime, fetchList, getAllCachedData] ); const refreshWatchingList = useCallback(