FlatList is a PureComponent: when its parent re-renders, it re-renders only if one of its own props changed. That is fast, and it causes a classic bug. If renderItem depends on something that is not a prop, such as a selected id kept in a store, the list never learns that it changed. extraData is the escape hatch: pass the value, or a version number, and the list re-renders when it changes. This demo keeps the selection in a plain object and bumps version on every tap:
const store = { selectedId: 0 }; // state kept outside React, as a store library would
const USE_EXTRA_DATA = true;
const keyExtractor = (b: Book) => String(b.id);
// ...
const renderItem = useCallback(({ item }: { item: Book }) => (
<LoggedRow book={item} selected={item.id === store.selectedId} onPress={select} />
), [select]);
return (
<FlatList data={books} keyExtractor={keyExtractor} renderItem={renderItem}
extraData={USE_EXTRA_DATA ? version : undefined} />LoggedRow is a memoized BookRow that logs each render. After the first six renders, a tap on Salt and Saffron logged one line with extraData and nothing without it, where the row also stayed unhighlighted:
LOG render book 3, selected=true
Only the changed row rendered, because memo skipped the rest. A first version of this demo passed keyExtractor={(b) => String(b.id)} inline, and then the row highlighted either way: the new function was a changed prop, so the list re-rendered on every parent render and hid the bug. Stable props make FlatList cheap; extraData tells it when stability is lying.