Skip to content

Example showing need for FlatList extraData prop is misleading #5216

Description

@mdj-uk

Basically #1529 from 2019 which was closed due to inactivity.

Related: #2634 which attempted to address this but unfortunately received no attention.

Description

The second example at https://reactnative.dev/docs/flatlist#example is introduced with

By passing extraData={selectedId} to FlatList we make sure FlatList itself will re-render when the state changes. Without setting this prop, FlatList would not know it needs to re-render any items because it is a PureComponent and the prop comparison will not show any changes.

But if you delete the extraData prop everything works fine. Since renderItem closes over selectedId, it is recreated whenever selectedId changes (or on every single render if the react compiler is disabled), hence FlatList receives a new prop and re-renders properly as you'd expect.

Which makes me wonder: is there ever a need for extraData? Was it once necessary (perhaps in class component days, or old react native architecture) and is no longer needed?

I'm struggling to think of a scenario in which it's needed, other than an escape hatch for syncing with something outside react, or to opt back in to reactivity after making manual memoizations.

Currently the docs suggest it's necessary to make basic reactivity work, which is confusing.

Documentation version

0.87

Activity

  1. theprantadutta commented on Aug 29, 2026

    @theprantadutta
    Contributor

    I dug through the current FlatList / VirtualizedList source to try to answer the "is extraData ever needed?" question, since that determines what the fix should say. Short version: you're right that the sentence is misleading, but extraData isn't vestigial — it just doesn't do what the docs claim it does.

    Why the example works without it. In that example renderItem is an inline closure over selectedId, so it's a new function identity on every render. By default FlatList passes the renderer straight through:

    // FlatList.js
    const renderer = strictMode ? this._memoizedRenderer : this._renderer;

    With strictMode unset, _renderer runs on every render and VirtualizedList receives new item-render props regardless of extraData. So removing it changes nothing here — exactly what you observed.

    Where it is load-bearing. Two places I can find:

    1. extraData is an argument to _renderer(...), and _memoizedRenderer is memoizeOne(this._renderer). When strictMode is enabled (documented as "Enable an optimization to memoize the item renderer to prevent unnecessary rerenders"), the memoized renderer is only rebuilt when one of its arguments changes — extraData among them. With a stable renderItem (module-scope, or useCallback with stable deps) plus strictMode, extraData is what tells the list something it renders from has changed.
    2. VirtualizedList.componentDidUpdate compares data and extraData to reset the viewability cache so onViewableItemsChanged fires again. That path is independent of item rendering.

    So the accurate framing is roughly: extraData tells the list that something renderItem depends on changed, when that change isn't visible in data or in the identity of renderItem itself. The current text instead says the list "would not know it needs to re-render any items because it is a PureComponent", which is the part that doesn't hold up — and it reads as though basic reactivity depends on it.

    What I'd propose, if a maintainer agrees with the direction: rewrite that bullet along the lines above, and either drop extraData from the example or add a one-line note that it's redundant there because renderItem is inline. I'd rather not guess how much strictMode / viewability detail belongs in a beginner-facing example, which is the one thing I'd want steering on.

    Happy to open that PR straight away — say the word and which shape you'd prefer. (@Simek, you reviewed my last docs PR here, so tagging you in case this is on your patch.)

    Caveat on my evidence: this is reading current main source, not a runtime repro — I don't have an Android/iOS setup to hand. If anything above doesn't match observed behaviour, trust the runtime over me.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions