Skip to content

fix: call function renderItem directly to prevent remount on data updates#441

Open
Daniel-Griffiths wants to merge 1 commit into
LegendApp:mainfrom
Daniel-Griffiths:fix/renderitem-remount-regression
Open

fix: call function renderItem directly to prevent remount on data updates#441
Daniel-Griffiths wants to merge 1 commit into
LegendApp:mainfrom
Daniel-Griffiths:fix/renderitem-remount-regression

Conversation

@Daniel-Griffiths
Copy link
Copy Markdown

@Daniel-Griffiths Daniel-Griffiths commented May 12, 2026

Problem

Since 50ee631, getRenderedItem always uses React.createElement(renderItem, itemProps). This treats the renderItem function reference as a React component type. When the reference changes between renders (e.g. inline callbacks, or callbacks with deps that change on data updates), React sees a different component type and unmounts/remounts the entire cell subtree.

In practice this causes all images and content in a list to flash on any state change, because every cell is fully destroyed and recreated.

ScreenRecording_05-12-2026.5-53-50.PM_1.mov

Fix

Restore the isFunction check that was removed in 50ee631. Plain functions are called directly as renderItem(itemProps).

Tradeoff

Function components passed directly as renderItem that use hooks internally will no longer work as they need to be wrapped in a callback, so this PR will likely need a few more changes before it's ready to merge.

Also I had to modify quite a few of the tests to get it to pass which I don't feel super confident about, ideally we'd want to try and support both uses cases while still fixing the rerender issue. 😅

…ates

When renderItem is passed as a plain function (the common callback pattern),
using React.createElement treats the function reference as a component type.
If that reference changes between renders (e.g. on a data update like voting),
React sees a different component type and unmounts/remounts the entire cell
subtree, causing visible flashing of images and other content.

Restore the isFunction check so plain functions are called directly, while
class components still go through createElement.
Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 16edeaf6fe

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

// Always render through React so function components can safely use Hooks.
renderedItem = React.createElement(renderItem, itemProps) as React.ReactNode;
renderedItem = (
isFunction(renderItem) ? renderItem(itemProps) : React.createElement(renderItem, itemProps)
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Preserve React component rendering path for renderItem

Do not invoke renderItem directly when it may be a React component: isFunction(renderItem) is true for both function components and class components, so the React.createElement branch is effectively unreachable. This regresses supported renderItem values from the declared API (React.ComponentType), causing class components to throw when called as plain functions and hook-using function components to fail outside React’s render lifecycle.

Useful? React with 👍 / 👎.

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant