오늘은 React-Navtive FlatList에 대해 알아보자!
FlatList는 많은 양의 스크롤이 필요한 리스트 아이템을 보여주고자 할 때 쓰이는 리액트네이티브 컴포넌트이다
JS에서의 map
함수 역할과 비슷하나 더 많은 기능을 내포하고 있어 react-native에서 많이 쓰이는 컴포넌트이다.
<ScrollView>
와 함께 주로 사용되는데 사용용도에 차이점이 조금 있다.<ScrollView>
는 데이터가 화면에 벗어났을 때 단순히 Scroll을 생성하여 사용자와의 상호작용(swipe)을 통해 벗어난 부분을 볼 수 있게 해주는 데이 목적이 있다
출력해야 하는 데이터가 고정적이고 많지 않을 때 간단하게 사용할 수 있는 컴포넌트
이에 반해<FlatList>
는 더 많은 기능이 있다.ScrollView
와 같이 데이터가 화면에 벗어났을 때도 Scroll을 생성하지만, 한 번에 모든 데이터를 렌더링하지 않고 화면에 보여지는 부분(혹은 설정한 수만큼의 데이터)만 렌더링한다는 차이가 있다.
그렇기에 데이터의 길이가 가변적이고, 데이터의 양을 예측할 수 없는 경우 (API를 통해 외부에서 크기를 알 수 없는 데이터를 가져오는 경우)에 사용하기 적절하다.
data
와 renderItem
이다.data
는 만들고자 하는 리스트의 soucre를 담는 prop이다.renderItem
은 data
로 받은 소스들 그 각각의 item
들을 render시켜주는 콜백함수이다. keyExtractor
는 ReactJS map함수에서 key={idex} 와 해줬듯이 각각의 item에 고유의 키를 주는 것이라 생각하면 된다.keyExtractor={(item, index) => index.toString()}
const renderItem = ({ item }) => {
return (
<View>
<View>
<Text>user id: {item.userId}</Text>
</View>
<View>
<Text>id: {item.id}</Text>
</View>
<View>
<Text>title: {item.title}</Text>
</View>
<View>
<Text>body: {item.body}</Text>
</View>
</View>
);
};
const LIMIT = 11;
export default function App() {
const [data, setData] = useState([]);
const [offset, setOffset] = useState(0);
const [loading, setLoading] = useState(false);
const getData = () => {
setLoading(true);
fetch("http://jsonplaceholder.typicode.com/posts")
.then((res) => res.json())
.then((res) => setData(res))
};
useEffect(() => {
getData();
}, []);
return (
<SafeAreaView style={styles.container}>
<FlatList
data={data}
renderItem={renderItem}
keyExtractor={(item) => String(item.id)}
/>
</SafeAreaView>
);
}
pagenation은 많은 양의 로드할 데이터가 앱에 있을 때 있어야할 다른 중요한 functionality이다.
왜냐면 실제로 우리는 많은 양의 데이터를 한 번에 로드할 수 없기 때문이다. 그렇게 하면 우리의 앱은 느려질 뿐만 아니라 퍼포먼스와 UX적으로 비효율적(inefficient)으로 된다.
FlatList에서는 onEndReached를 통해 유저가 화면의 끝에 도달했을 때 새로운 데이터를 호출(fetch)하여 해당 비효율을 해결할 수 있게한다.
const LIMIT = 11;
export default function App() {
const [data, setData] = useState([]);
const [offset, setOffset] = useState(0);
const [loading, setLoading] = useState(false);
const getData = () => {
setLoading(true);
fetch("http://jsonplaceholder.typicode.com/posts")
.then((res) => res.json())
.then((res) => setData(data.concat(res.slice(offset, offset + LIMIT))))
.then(() => {
setOffset(offset + LIMIT);
setLoading(false);
})
.catch((error) => {
setLoading(false);
Alert.alert("에러가 났습니다");
});
};
useEffect(() => {
getData();
}, []);
const onEndReached = () => {
if (loading) {
return;
} else {
getData();
}
};
여기서
if(loading) { return }
을 통해 로딩 중 계속 호출(fetch) 되는 것을 막는다.
return (
<SafeAreaView style={styles.container}>
<FlatList
data={data}
renderItem={renderItem}
keyExtractor={(item) => String(item.id)}
onEndReached={onEndReached}
onEndReachedThreshold={0.8}
ListFooterComponent={loading && <ActivityIndicator />}
/>
</SafeAreaView>
);
}
여기서 onEndReachedThreshold
는 onEndReach의 지점을 설정해주는 것이라 할 보면 된다. 기본이 1이라고 한다면 제일 유저의 화면 끝에서 실행된다면 0.8은 해당 화면에서 80%가 됐을 때 실행된다고 보면된다.
<ActiveIndicator>
는 아래와 같은 RN에서 제공하는 로딩 컴포넌트이다.
const FlatListItemSeperator = () => {
return (
<View
style={{
height: 1,
width: "100%",
backgroundColor: "#000",
}}
/>
);
};
ItemSeparatorComponent={FlatListItemSeperator}
을 통해 각 아이템 간의 커스텀을 줄 수도 있다
또한 ListHeaderComponent
와 ListFooterComponent
활용해 header와 footer에 어떤 요소를 넣을지 커스텀/결정할 수 있다.
참고문서