Module 8: Advanced UI Components

Build advanced UI components and implement smooth animations in React Native.

Back to Course|5 hours|Advanced

Advanced UI Components

Build advanced UI components and implement smooth animations in React Native.

Progress: 0/4 topics completed0%

Select Topics Overview

FlatList & SectionList

Master FlatList and SectionList components for efficient rendering of large lists in React Native applications

Content by: Ronak Macwan

React Native Developer

Connect

FlatList Component

FlatList is a performant interface for rendering basic, flat lists with support for most common use cases like horizontal mode, header/footer, pull-to-refresh, and more.

FlatList Properties

  • data - Array of items to render
  • renderItem - Function to render each item
  • keyExtractor - Function to extract unique keys
  • getItemLayout - Optimize performance with known item dimensions
  • onEndReached - Callback when reaching the end
  • refreshing - Pull-to-refresh state
  • onRefresh - Pull-to-refresh callback

SectionList Component

SectionList is a performant interface for rendering sectioned lists with support for sticky section headers, section separators, and more.

Performance Optimization

Use getItemLayout, keyExtractor, and removeClippedSubviews for optimal performance with large datasets.

Advanced FlatList Example

Code Example
import React, { useState, useEffect } from 'react';
import { View, Text, FlatList, TouchableOpacity, StyleSheet, RefreshControl, ActivityIndicator } from 'react-native';

const AdvancedFlatList = () => {
  const [data, setData] = useState([]);
  const [loading, setLoading] = useState(false);
  const [refreshing, setRefreshing] = useState(false);
  const [page, setPage] = useState(1);

  const loadData = async (pageNum = 1, isRefresh = false) => {
    if (isRefresh) {
      setRefreshing(true);
    } else {
      setLoading(true);
    }

    try {
      // Simulate API call
      await new Promise(resolve => setTimeout(resolve, 1000));
      const newData = Array.from({ length: 20 }, (_, i) => ({
        id: (pageNum - 1) * 20 + i + 1,
        title: `Item ${(pageNum - 1) * 20 + i + 1}`,
        subtitle: `This is subtitle for item ${(pageNum - 1) * 20 + i + 1}`,
        timestamp: new Date().toISOString(),
      }));

      if (isRefresh) {
        setData(newData);
        setPage(1);
      } else {
        setData(prev => [...prev, ...newData]);
        setPage(pageNum);
      }
    } catch (error) {
      console.error('Error loading data:', error);
    } finally {
      setLoading(false);
      setRefreshing(false);
    }
  };

  useEffect(() => {
    loadData();
  }, []);

  const onRefresh = () => {
    loadData(1, true);
  };

  const onEndReached = () => {
    if (!loading) {
      loadData(page + 1);
    }
  };

  const renderItem = ({ item, index }) => (
    <TouchableOpacity style={styles.item}>
      <Text style={styles.itemTitle}>{item.title}</Text>
      <Text style={styles.itemSubtitle}>{item.subtitle}</Text>
      <Text style={styles.itemTimestamp}>
        {new Date(item.timestamp).toLocaleString()}
      </Text>
    </TouchableOpacity>
  );

  const renderHeader = () => (
    <View style={styles.header}>
      <Text style={styles.headerTitle}>Advanced FlatList</Text>
      <Text style={styles.headerSubtitle}>Pull to refresh, infinite scroll</Text>
    </View>
  );

  const renderFooter = () => {
    if (!loading) return null;
    return (
      <View style={styles.footer}>
        <ActivityIndicator size="small" color="#2196F3" />
        <Text style={styles.footerText}>Loading more...</Text>
      </View>
    );
  };

  const renderEmpty = () => (
    <View style={styles.empty}>
      <Text style={styles.emptyText}>No data available</Text>
    </View>
  );

  return (
    <FlatList
      data={data}
      renderItem={renderItem}
      keyExtractor={item => item.id.toString()}
      ListHeaderComponent={renderHeader}
      ListFooterComponent={renderFooter}
      ListEmptyComponent={renderEmpty}
      refreshControl={
        <RefreshControl
          refreshing={refreshing}
          onRefresh={onRefresh}
          colors={['#2196F3']}
          tintColor="#2196F3"
        />
      }
      onEndReached={onEndReached}
      onEndReachedThreshold={0.5}
      removeClippedSubviews={true}
      maxToRenderPerBatch={10}
      windowSize={10}
      initialNumToRender={10}
      getItemLayout={(data, index) => ({
        length: 80,
        offset: 80 * index,
        index,
      })}
      style={styles.container}
    />
  );
};

const styles = StyleSheet.create({
  container: {
    flex: 1,
    backgroundColor: '#f5f5f5',
  },
  header: {
    backgroundColor: '#2196F3',
    padding: 20,
    alignItems: 'center',
  },
  headerTitle: {
    fontSize: 24,
    fontWeight: 'bold',
    color: 'white',
    marginBottom: 5,
  },
  headerSubtitle: {
    fontSize: 16,
    color: 'rgba(255, 255, 255, 0.8)',
  },
  item: {
    backgroundColor: 'white',
    padding: 15,
    marginHorizontal: 10,
    marginVertical: 5,
    borderRadius: 8,
    shadowColor: '#000',
    shadowOffset: { width: 0, height: 2 },
    shadowOpacity: 0.1,
    shadowRadius: 4,
    elevation: 3,
  },
  itemTitle: {
    fontSize: 18,
    fontWeight: 'bold',
    color: '#333',
    marginBottom: 5,
  },
  itemSubtitle: {
    fontSize: 14,
    color: '#666',
    marginBottom: 5,
  },
  itemTimestamp: {
    fontSize: 12,
    color: '#999',
  },
  footer: {
    padding: 20,
    alignItems: 'center',
  },
  footerText: {
    marginTop: 10,
    fontSize: 14,
    color: '#666',
  },
  empty: {
    flex: 1,
    justifyContent: 'center',
    alignItems: 'center',
    padding: 40,
  },
  emptyText: {
    fontSize: 16,
    color: '#999',
  },
});

export default AdvancedFlatList;
Swipe to see more code

🎯 Practice Exercise

Test your understanding of this topic:

Ready for the Next Module?

Continue your learning journey and master the next set of concepts.

Continue to Module 9