Loading source
Pulling the file list, source metadata, and syntax-aware rendering for this listing.
Source from repo
Build performant React Native and Expo apps with best practices for lists, animations, and navigation
Files
Skill
Size
Entrypoint
Format
Open file
Syntax-highlighted preview of this file as included in the skill package.
rules/list-performance-function-references.md
1---2title: Optimize List Performance with Stable Object References3impact: CRITICAL4impactDescription: virtualization relies on reference stability5tags: lists, performance, flatlist, virtualization6---78## Optimize List Performance with Stable Object References910Don't map or filter data before passing to virtualized lists. Virtualization11relies on object reference stability to know what changed—new references cause12full re-renders of all visible items. Attempt to prevent frequent renders at the13list-parent level.1415Where needed, use context selectors within list items.1617**Incorrect (creates new object references on every keystroke):**1819```tsx20function DomainSearch() {21const { keyword, setKeyword } = useKeywordZustandState()22const { data: tlds } = useTlds()2324// Bad: creates new objects on every render, reparenting the entire list on every keystroke25const domains = tlds.map((tld) => ({26domain: `${keyword}.${tld.name}`,27tld: tld.name,28price: tld.price,29}))3031return (32<>33<TextInput value={keyword} onChangeText={setKeyword} />34<LegendList35data={domains}36renderItem={({ item }) => <DomainItem item={item} keyword={keyword} />}37/>38</>39)40}41```4243**Correct (stable references, transform inside items):**4445```tsx46const renderItem = ({ item }) => <DomainItem tld={item} />4748function DomainSearch() {49const { data: tlds } = useTlds()5051return (52<LegendList53// good: as long as the data is stable, LegendList will not re-render the entire list54data={tlds}55renderItem={renderItem}56/>57)58}5960function DomainItem({ tld }: { tld: Tld }) {61// good: transform within items, and don't pass the dynamic data as a prop62// good: use a selector function from zustand to receive a stable string back63const domain = useKeywordZustandState((s) => s.keyword + '.' + tld.name)64return <Text>{domain}</Text>65}66```6768**Updating parent array reference:**6970Creating a new array instance can be okay, as long as its inner object71references are stable. For instance, if you sort a list of objects:7273```tsx74// good: creates a new array instance without mutating the inner objects75// good: parent array reference is unaffected by typing and updating "keyword"76const sortedTlds = tlds.toSorted((a, b) => a.name.localeCompare(b.name))7778return <LegendList data={sortedTlds} renderItem={renderItem} />79```8081Even though this creates a new array instance `sortedTlds`, the inner object82references are stable.8384**With zustand for dynamic data (avoids parent re-renders):**8586```tsx87const useSearchStore = create<{ keyword: string }>(() => ({ keyword: '' }))8889function DomainSearch() {90const { data: tlds } = useTlds()9192return (93<>94<SearchInput />95<LegendList96data={tlds}97// if you aren't using React Compiler, wrap renderItem with useCallback98renderItem={({ item }) => <DomainItem tld={item} />}99/>100</>101)102}103104function DomainItem({ tld }: { tld: Tld }) {105// Select only what you need—component only re-renders when keyword changes106const keyword = useSearchStore((s) => s.keyword)107const domain = `${keyword}.${tld.name}`108return <Text>{domain}</Text>109}110```111112Virtualization can now skip items that haven't changed when typing. Only visible113items (~20) re-render on keystroke, rather than the parent.114115**Deriving state within list items based on parent data (avoids parent116re-renders):**117118For components where the data is conditional based on the parent state, this119pattern is even more important. For example, if you are checking if an item is120favorited, toggling favorites only re-renders one component if the item itself121is in charge of accessing the state rather than the parent:122123```tsx124function DomainItemFavoriteButton({ tld }: { tld: Tld }) {125const isFavorited = useFavoritesStore((s) => s.favorites.has(tld.id))126return <TldFavoriteButton isFavorited={isFavorited} />127}128```129130Note: if you're using the React Compiler, you can read React Context values131directly within list items. Although this is slightly slower than using a132Zustand selector in most cases, the effect may be negligible.133