RN에서 스크롤 영역이 필요하다면 이를 구현하는 방법에는 ScrollView
를 사용하는 방법과 FlatList
를 사용하는 방법이 있다. ScrollView
의 경우에는 한번에 전체 영역을 렌더링하고, FlatList
는 화면에 보일 만큼, 또는 설정한 수 만큼만 초기 렌더링한다는 특징이 있다. 이러한 특성에 맞추어 각 목적에 따라 필요한 컴포넌트를 사용하여 구현을 하면 된다.
그러나 불행히도, 때로는 구현 요구사항이 훨씬 복잡할 때가 있다. 전체 스크롤 영역과 별개로 그 안에서 일부 영역만 추가로 스크롤이 필요한 구현 사항의 경우 어떨까?
ScrollView
안에 ScrollView
를 넣는다면 쉽게 해결되겠지만, (안드로이드의 경우 nestedScrollEnabled
옵션 필요) ScrollView
영역에 FlatList
를 넣으려 하는 경우 약간의 불행이 시작된다.
VirtualizedLists should never be nested inside plain ScrollViews with the same orientation because it can break windowing and other functionality - use another VirtualizedList-backed container instead.
위와 같은 에러를 한번 쯤은 마주해 본 사람이 꽤 많을 것이다.
이는 단순하게 FlatList
의 ListHeaderComponent
, ListFooterComponent
옵션을 잘 사용하거나, FlatList
안에 FlatList
를 넣도록 수정하면 해결되는 오류이기는 하다. 나는 주로 후자의 방식으로 개발해 왔다.
<FlatList
data={[]}
renderItem={null}
ListEmptyComponent={
<>
<View>
<Text>Something to add above...</Text>
</View>
<FlatList data={data} {...} />
<View>
<Text>Something to add below...</Text>
</View>
</>
}
/>
그러다가 필요할 때마다 일일이 위와 같이 코드를 작성하는 게 귀찮아져, 어느 날 그냥 공통 컴포넌트를 하나 작성했다. 기존 ScrollView
처럼 원하는 컴포넌트를 children
으로 넘길 수 있게 하고, 내가 개발하던 프로젝트의 경우 Animated FlatList를 사용하거나 FlatList 내에 ref 옵션을 넘겨야하는 경우가 많았어서 이를 고려한 옵션까지 추가해 두었다.
import { memo, ReactNode, RefObject } from "react";
import { Animated, FlatList, VirtualizedListWithoutRenderItemProps } from "react-native";
interface FlatListScrollViewProps {
children?: ReactNode;
isAnimated?: boolean;
scrollRef?: RefObject<FlatList>;
}
/**
* ScrollView 안에 FlatList를 넣어야할 때에 대신 사용할 컴포넌트
*/
function FlatListScrollView<T>({
children,
isAnimated,
scrollRef,
...props
}: FlatListScrollViewProps & VirtualizedListWithoutRenderItemProps<T>) {
if (isAnimated) {
return (
<Animated.FlatList {...props} ref={scrollRef} data={[]} renderItem={null} ListEmptyComponent={<>{children}</>} />
);
}
return <FlatList {...props} ref={scrollRef} data={[]} renderItem={null} ListEmptyComponent={<>{children}</>} />;
}
export default memo(FlatListScrollView);
FlatList
내에 일정한 height를 가진 FlatList
를 같은 방향으로 스크롤하도록 넣는 경우, nested scroll이 제대로 동작하지 않는다. 검색해 보면 비슷한 이슈의 글들을 어렵지 않게 찾을 수 있는데, 대부분 react-native-gesture-handler
라이브러리의 FlatList
를 사용하거나, ScrollView
로 변경해서 해결했다고 한다.
때문에 나는 다른 방향 스크롤이 들어가는 경우에만 위와 같은 코드를 사용하고, 같은 방향 스크롤이 필요한 경우에는 상황에 따라 FlatList
또는 ScrollView
root 내에 ScrollView
를 사용하였다.