Encodera

Thanks for reading!

Donate to the site — we work hard for you.

Contents

Lists with FlatList

A React Native screen does not scroll by itself: if there are more tasks than fit, the bottom ones are simply not visible. Lists use FlatList, a scrolling list that creates only the visible rows on the screen and therefore stays fast even with thousands of items.

The row component

Move the task row into a separate component:

// components/TaskItem.tsx
import { Pressable, StyleSheet, Text } from 'react-native';
 
export type Task = { id: string; title: string; done: boolean };
 
type Props = { task: Task; onToggle: (id: string) => void };
 
export default function TaskItem({ task, onToggle }: Props) {
  return (
    <Pressable onPress={() => onToggle(task.id)} style={styles.row}>
      <Text style={[styles.title, task.done && styles.done]}>{task.title}</Text>
    </Pressable>
  );
}
 
const styles = StyleSheet.create({
  row: { paddingVertical: 14, paddingHorizontal: 16 },
  title: { fontSize: 18 },
  done: { color: '#999', textDecorationLine: 'line-through' },
});

The Task type is now exported from here, and app/index.tsx can import it instead of declaring its own.

FlatList

Replace tasks.map(...) in app/index.tsx with a FlatList:

// app/index.tsx
import { FlatList } from 'react-native';
import TaskItem, { type Task } from '@/components/TaskItem';
 
<FlatList
  data={tasks}
  keyExtractor={(item) => item.id}
  renderItem={({ item }) => <TaskItem task={item} onToggle={toggleTask} />}
/>

Add twenty tasks and the list starts scrolling with your finger. The three main props:

PropWhat to pass
dataAn array of items
renderItemA function that receives item and index and returns a row
keyExtractorA function returning a unique string key

renderItem receives an object rather than the item itself, so the parameter is destructured: ({ item }).

Separators, header and an empty list

// app/index.tsx
<FlatList
  data={tasks}
  keyExtractor={(item) => item.id}
  renderItem={({ item }) => <TaskItem task={item} onToggle={toggleTask} />}
  ItemSeparatorComponent={() => <View style={{ height: 1, backgroundColor: '#eee' }} />}
  ListEmptyComponent={<Text style={{ padding: 16 }}>No tasks yet</Text>}
  ListHeaderComponent={<Text style={{ fontSize: 24, padding: 16 }}>My tasks</Text>}
/>

Thin grey lines appear between rows, and a heading at the top scrolls together with the list. While there are no tasks, "No tasks yet" is shown instead of the list.

Where to place a FlatList

A FlatList must have a limited height, otherwise it does not know what to scroll. It is usually placed in a root View with flex: 1, and the list takes the space left under the input field and the button.

Common mistake

Forgetting keyExtractor when the items have no key field. React Native shows a warning about keys, and rows may re-render incorrectly when tasks are deleted. If the data has an id field, always pass keyExtractor={(item) => item.id}; the key must be a string, so numbers are converted with String(item.id).

Thanks for reading!

Donate to the site — we work hard for you.