renderItem receives one object, not the item itself: { item, index, separators }, where separators holds highlight(), unhighlight() and updateProps() for restyling the separators around that row. Keep the function small and give the real work to a memoized row component, so an update re-renders only the rows whose props changed. Here is BookNest's row, used by every list that follows:
type Props = { book: Book; selected?: boolean; onPress?: (id: number) => void };
export const ROW_HEIGHT = 96;
function BookRow({ book, selected = false, onPress }: Props) {
return (
<Pressable style={[s.row, selected && s.selected]} onPress={() => onPress?.(book.id)}>
<Image source={cover(book)} style={s.cover} />
<View style={{ flex: 1 }}>
<Text style={s.title} numberOfLines={1}>{book.title}</Text>
<Text style={s.meta}>{book.author} · {book.genre}</Text>
<Text style={s.price}>${book.price.toFixed(2)}</Text>
</View>
</Pressable>
);
}
export default memo(BookRow); // re-render only when book, selected or onPress changekeyExtractor gives each row the stable identity React 7,897 needs to match old rows to new ones, exactly as key does in a web list (MERN Stack Development). Leave it out and the default in @react-native/virtualized-lists returns item.key, then item.id, then String(index), silently: a FlatList of plain title strings rendered without a single warning in Metro. Index keys work until the data changes; insert a book at the top and every key shifts, so React reuses the wrong components and per-row state moves with them. Return a string, such as String(b.id), and define renderItem and keyExtractor outside the component or in useCallback, because a new function is a new prop (extraData and Re-Rendering).