DEV Community

Cover image for React Native Optimization: Stop Recreating Functions in Large Lists (FlatList, Flashlist, LegendList...)
john mbugua
john mbugua

Posted on

React Native Optimization: Stop Recreating Functions in Large Lists (FlatList, Flashlist, LegendList...)

A practical guide to optimizing list performance in React Native by memoizing renderItem and callbacks to prevent unnecessary re-renders.

When a React Native screen starts feeling (Heavy, janky scroll, dropped frames), it’s often not one big issue, it’s lots of small ones stacking up.One of the most common (and easiest to fix) is inline functions in large lists. This applies to FlatList, SectionList, LegendList, Shopify FlashList, and pretty much any component that renders many rows.

Why inline functions hurt list performance

In JavaScript, functions are objects. That means every time your component re-renders, any inline function you write is created again with a new reference.

<LegendList
  data={data}
  renderItem={({ item }) => (
    <Row
      item={item}
      onPress={() => handlePress(item.id)} // new function every render
    />
  )}
/>
Enter fullscreen mode Exit fullscreen mode

Even if item didn’t change, onPress={() => ...} is a brand new function each render.

Why does that matter?

  • If Row is memoized, it can still re-render because a prop changed (new function reference).

  • In big lists, that means more work for the JS thread, more layout work, and a higher chance of scroll stutters.

The goal is not “never use inline functions.” The goal is: don’t create new function references for every row, every render, especially when the list is large.

Memoize renderItem with useCallback

First, make renderItem stable.

const renderItem = useCallback(({ item }) => {
  return <Row item={item} />;
}, []);

return (
  <FlatList
    data={data}
    renderItem={renderItem}
    keyExtractor={(item) => item.id}
  />
);

Enter fullscreen mode Exit fullscreen mode

Now renderItem won’t be recreated on every screen re-render.

Cool — but what about row actions?

At this point renderItem is stable, which is a big win.

But sometimes items usually have actions:

  • tap to open details
  • long-press to show a menu
  • swipe / delete
  • “call customer”, “copy code”, etc.

And this is where many apps accidentally bring the problem back.

The common trap: inline callbacks per row

You will often have/see something like this:

const renderItem = useCallback(({ item }) => (
  <Row
    item={item}
    onPress={() => handleOpen(item.id)}
    onLongPress={() => handleMenu(item)}
  />
), [handleOpen, handleMenu]);
Enter fullscreen mode Exit fullscreen mode

Even though renderItem is memoized, those () => ... functions are still created when renderItem runs. That means every row receives new callback references, which can cause Row to re-render even when the UI didn’t actually change.

So what's the next goal:

Keep handlers stable, and avoid generating new functions for each item inside renderItem.

The better pattern: pass stable handlers, let the row provide the id Instead of binding item inside renderItem, pass a stable function down and let the row call it with its own identifier.

const onPressItem = useCallback((id: string) => {
  handleOpen(id);
}, [handleOpen]);

const onLongPressItem = useCallback((id: string) => {
  handleMenu(id);
}, [handleMenu]);

const renderItem = useCallback(({ item }) => {
  return (
    <Row
      item={item}
      onPressItem={onPressItem}
      onLongPressItem={onLongPressItem}
    />
  );
}, [onPressItem, onLongPressItem]);

Enter fullscreen mode Exit fullscreen mode

Now, as long ashandleOpen and handleMenu don’t change, the function references stay stable.

Conclusion

When you are working with large lists in React Native, the small things matter. Memoizing renderItem and avoiding inline callbacks helps keep references stable, which means fewer unnecessary row re-renders and smoother scrolling.

You don’t have to over optimize every screen, but for list heavy UIs, these changes are some of the highest wins you can make.

Acknowledgement: Big thanks to the team at Callstack for their webinar on React Native optimization a lot of the ideas here clicked for me through that talk.

Top comments (0)