In this article, I’m not going to repeat the usual advice about minimizing rendered content or caching images. No. We’re going to focus exclusively on real list optimization, regardless of what exactly needs to be rendered — because when a client says “I want it this way,” arguing about requirements is rarely productive.



Let’s start with the simplest possible example. Suppose we have an array:

```javascript
const list = [{ _id: 567456, label: «CodingChipmunks» }, ...]
```

What’s the easiest way to render a small list?
Correct — using `.map()`, which returns a new array that can be rendered directly.

For example:

```javascript
render() {
return (
{list.map((item) => (
{item.label}

))}

)
}
```

Looks fine? **Absolutely not. Never do this.**

* First, every rendered item must have a stable `key`.
* And please, do not use the array index as a key. I’ll explain why later.
* Second, this code becomes unreadable very quickly.
* Third, avoid anonymous functions inside render paths.

A much cleaner approach would be:

```javascript
renderItem = (item) => (
{item.label}

)

render() {
return (
{list.map((item) => this.renderItem(item))}

)
}
```

Or even better:

```javascript
const Item = ({ item }) => (
{item.label}

)

class List extends React.Component {
render() {
return (
{list.map((item) => (
/>
))}

)
}
}
```

Much better now? Not quite.

There’s still one important optimization left: avoid anonymous callbacks inside `.map()`.

Every time `render()` is called, a brand-new function instance gets created for every list item — regardless of whether the component actually changed or not.

Never. I repeat: never do this in performance-sensitive code.

I constantly run into projects written this way. Sometimes even experienced developers inherit a codebase like this and simply continue adding more of the same.

And the reasoning is usually:

> “Why bother improving it if it was already messy before?”

Which immediately raises the question:
“What? Why? Don’t you want to leave the project in a better state than you found it?”

The fix is trivial:

```javascript
render() {
return (
{list.map(this.renderItem)}

)
}
```

And the final touch: use `PureComponent` for rendering individual items.

For example:

```javascript
class Item extends React.PureComponent {

}
```

That alone can eliminate a significant number of unnecessary re-renders.

— # What About Large and Complex Lists?

Now that we’ve covered basic rendering, let’s move on to real-world scenarios: large datasets, dozens or hundreds of items, smooth scrolling requirements, and dynamic updates.

The answer is obviously React Native’s `FlatList`.

But simply switching to `FlatList` is not enough.

Let’s break down how to use it properly.

Basic setup:

```javascript
const list = [loyaltyCard1, loyaltyCard2, ..., loyaltyCard100]

renderItem = ({ item: loyaltyCard, index }) => (...)

keyExtractor = (loyaltyCard) => loyaltyCard._id

render() {
return (
<FlatList
keyExtractor={this.keyExtractor}
data={list}
renderItem={this.renderItem}
/>
)
}
```

With `FlatList`, there’s no need to specify `key` manually for every item. Instead, use `keyExtractor`, which should return a unique identifier for each element.

Also note that `renderItem` receives an object with three properties:

1. `item` — the actual array element
2. `index` — the item index
3. `separators` — separator helpers (not relevant for now)

Is that all the optimization we need?
Not even close.

— ## `extraData`

If you need to force the list to re-render without mutating the original `data` array, use `extraData`.

Whenever `extraData` changes, `FlatList` triggers an update.

— ## `getItemLayout`

If every item has a known fixed height, use `getItemLayout`.

This allows React Native to skip runtime layout measurements entirely.

From the official documentation:

```javascript
getItemLayout={(data, index) => ({
length: ITEM_HEIGHT,
offset: ITEM_HEIGHT * index,
index,
})}
```

This is one of the most impactful optimizations for large lists.

— ## What If the Height Is Unknown but Static?

If the height isn’t known upfront but remains constant after rendering, you can measure it once using `onLayout`.

```javascript
onLayout = (event) => {
const { x, y, width, height } = event.nativeEvent.layout
// do something with layout
}

renderItem = ({ item, index }) => (
/>
)
```

— ## Rendering Large Datasets Incrementally

If the dataset is huge and initial rendering causes frame drops or scroll jank, the following props become critical:

* `initialNumToRender`
* `maxToRenderPerBatch`

These allow content to be rendered incrementally instead of all at once.

### `initialNumToRender`

Defines how many items should be rendered initially.

A good rule of thumb:
render enough items to fully cover the viewport — plus a small buffer.

### `maxToRenderPerBatch`

Defines how many additional items are rendered in subsequent batches.

Important:
if you’re implementing infinite scrolling via `onEndReached` and `onEndReachedThreshold`, make sure this value matches your API page size. Otherwise, you’ll eventually run into duplicated items and rendering inconsistencies.

— # Experimental and Risky Optimizations

Now let’s move on to optimizations that can improve performance, but also introduce rendering artifacts or unstable behavior.

Use them carefully.

— ## `removeClippedSubviews`

What if we completely remove items outside the visible viewport?

Sounds great in theory.

That’s exactly what `removeClippedSubviews` does.

```javascript
removeClippedSubviews={true}
```

When enabled, views outside the visible area are detached from the native rendering hierarchy.

However, be extremely careful.

Under aggressive scrolling conditions, users may briefly see empty gaps before content gets mounted again.

This optimization works best when combined with `getItemLayout`.

You can also fine-tune how aggressively offscreen content gets clipped depending on your use case.

— That’s probably enough for one article.

There are several additional techniques for optimizing lists in React Native, but many of them introduce trade-offs that are highly context-dependent.

I intentionally avoided covering those here to keep the article focused on practical, production-grade optimizations rather than premature micro-optimizations that often create more problems than they solve.