draggable-flatlist 스크롤 위치 저장 정리

상황 설명

1.
운동 라이브러리와 같은 UI상에서 각 카테고리별로 스크롤이 따로 움직이도록 작업 (BUR-3956)
2.
카테고리별로 스크롤 위치를 기억하게 하는것으로 구현하고자함
3.
리액트 네이티브 자체적으로 사용하는 flatlist는 문제 없이 구현됐지만, draggable flatlist에 동일한 방법을 적용할 경우 스크롤 위치가 계속 어긋나는 이슈가 생김 (+ 퍼포먼스 저하)

Flatlist 구현 코드

ExerciseLibraryScreen.tsx에 스크롤 위치 저장
const listRef = useRef(null); const scrollPositions = useRef({}); // 각 카테고리별 스크롤 위치 저장용 // 스크롤 할때마다 스크롤 위치 저장 const handleScroll = (event) => { scrollPositions.current[filteredResult?.bId] = event.nativeEvent.contentOffset.y; }; // 카테고리 변경 시 해당 카테고리에 맞는 스크롤 위치 적용 const handleBodyPartChange = (bId) => { requestAnimationFrame(() => { const y = scrollPositions.current[bId] || 0; listRef.current?.scrollToOffset({ offset: y, animated: false }); }); };
JavaScript
복사
ManageExerciseList.tsx (flatlist가 있는 컴포넌트)
<View style={{ flex: 1, paddingHorizontal: 16 }}> <FlatList // scroll ref값 (prop으로 전달 받음) ref={listRef} bounces windowSize={5} showsVerticalScrollIndicator data={listData} keyExtractor={(item) => `${item.eId.toString()}`} renderItem={({ item }) => ( <ExerciseLibraryListItem eId={item.eId} eTextId={item.eTextId} bId={item.bId} eName={item.eName} eInfoType={item.eInfoType} tId={item.tId} cnt={item?.cnt} drag={null} tooltip={tooltip} setTooltip={setTooltip} isKeyboardVisible={isKeyboardVisible} /> )} // 스크롤 할때마다 스크롤 위치 저장 (prop으로 전달 받음) onScroll={handleScroll} ListFooterComponent={() => <View style={{ height: 120 }} />} keyboardShouldPersistTaps='always' /> </View>
JavaScript
복사
BodyParSelector.tsx (카테고리 변경용 컴포넌트)
{exerciseBodyPart.map((bItem) => ( <View key={bItem.bId} style={{ minWidth: 56 }}> <TouchableOpacity hitSlop={{ top: 8, right: 8, bottom: 8, left: 8 }} onPress={() => { if (handleBodyPartChange) { // 카테고리 변경 시, 저장되어있던 스크롤 위치 적용 (prop으로 전달 받음) handleBodyPartChange(bItem.bId); } filterExerciseByBodypart(bItem.bId); }} style={bId === bItem.bId ? styles.buttonSelected : styles.buttonNormal} > <BFText name='B3' lightColor={bId === bItem.bId ? BFCOLOR.WHITE : BFCOLOR.GRAY110}> {bItem.bName} </BFText> </TouchableOpacity> </View> ))}
JavaScript
복사

Draggable Flatlist에 적용 시 문제점

1.
flatlist에서 사용됐던 onScroll을 사용할 수 없음. 해당 부분은 onScrollOffsetChange로 대체해서 사용함.
2.
onScrollOffsetChange를 사용할 경우, onScroll과 동일하게 현재 스크롤의 위치(offset)를 받을 수 있음.
3.
다만 여기서 문제는 카테고리 변경 시 저장된 스크롤의 위치를 적용할 때, 적용된 스크롤의 값이 실제 저장된 값과 상이함.

Draggable Flatlist 구현 코드

ExerciseManageScreen.tsx (운동 리스트 수정)
const categoryRef = useRef(filteredResult.bId); const listRef = useRef(null); const scrollPositions = useRef({}); // 각 카테고리별 스크롤 위치 저장용 categoryRef.current = filteredResult.bId; const isScrolled = useRef(SCROLL_DEFAULT); const handleScrollOffsetChange = (offset) => { if (isScrolled.current !== SCROLL_PROCESS) { scrollPositions.current[categoryRef.current] = offset; console.log('스크롤 offset 갱신', offset); } else { console.log('isScrolled 불가능 상태'); isScrolled.current = SCROLL_CHANGED; } }; // 카테고리 변경 시 해당 카테고리에 맞는 스크롤 위치 적용 const handleBodyPartChange = (bId) => { console.log('카테고리 변경', bId); const y = scrollPositions.current[bId] || 0; console.log('저장된 스크롤 값', scrollPositions.current[bId]); listRef.current.scrollToOffset({ offset: y, animated: false }); console.log('저장된 스크롤 값 적용'); isScrolled.current = SCROLL_PROCESS; console.log('isScrolled 값 변경'); };
JavaScript
복사
handleBodyPartChange 함수를 호출할 때 y값은 이전에 저장된 스크롤 위치의 값이 나온다.
다만 스크롤이 이동하는 중에 handleScrollOffsetChange 함수가 호출되고, 이때 받게되는 offset값은 저장된 스크롤 위치값과 다르다.
handleScrollOffsetChange 함수가 호출되며 저장된 스크롤 값이 갱신되는 것이 문제인가 싶어서 isScrolled 라는 ref값을 추가했지만, listRef.current.scrollToOffset({ offset: y, animated: false }); 이 명확하게 먼저 호출됐음에도 지속적으로 스크롤 위치가 맞지 않는 문제가 생김.
해당 코드 실행 시 console 내역
LOG 스크롤 offset 갱신 1760 // handleScrollOffsetChange 함수 LOG 카테고리 변경 2 // 가슴 카테고리로 변경 LOG 저장된 스크롤 값 undefined // 해당 카테고리는 아직 저장된 스크롤 위치가 없어서 0으로 적용 LOG 저장된 스크롤 값 적용 LOG isScrolled 값 변경 LOG isScrolled 불가능 상태 LOG 카테고리 변경 1 // 다시 하체 카테고리로 변경 LOG 저장된 스크롤 값 1760 // 저장된 스크롤 위치 확인 LOG 저장된 스크롤 값 적용 // listRef.current.scrollToOffset 호출 후 로그 찍음 LOG isScrolled 값 변경 // isScrolled의 값을 SCROLL_PROGRESS로 변경해서 handleScrollOffsetChange가 스크롤 위치 갱신 못 하게 막음 LOG isScrolled 불가능 상태 // handleScrollOffsetChange 함수 호출됐지만 갱신 불가능 -> 가능 상태로 변경 LOG 스크롤 offset 갱신 1328 // listRef.current.scrollToOffset로 적용한 스크롤 값과 다른 지점에서 스크롤이 멈춤
JavaScript
복사
해당 이슈에 대한 원인은대한 원인은 찾지 못 한 상태