DEV Community

Cover image for Why Your React Keyed Lists Still Re-render Unnecessarily
Ravali Peddi
Ravali Peddi

Posted on

Why Your React Keyed Lists Still Re-render Unnecessarily

Understanding React's Reconciliation & Keyed Lists

React optimizes UI updates through a process called reconciliation, where it efficiently updates the DOM based on state changes. One of the most well-known optimizations is using keys in lists to help React identify which items have changed, been added, or removed.

However, many developers assume that adding a unique key to list items completely prevents unnecessary re-renders. In reality, React may still re-render list items even when their keys remain stable. Let’s dive into why this happens and how to fix it.


1. When Do Keyed Lists Re-render Unnecessarily?

Even with correct keys, React can still trigger unnecessary re-renders due to:

(a) Parent Component Re-renders the List

If the parent component re-renders, React will re-evaluate all child components, including list items, even if their keys have not changed.

const ListComponent = ({ items }) => {
  return (
    <ul>
      {items.map((item) => (
        <ListItem key={item.id} item={item} />
      ))}
    </ul>
  );
};
Enter fullscreen mode Exit fullscreen mode

In this case, if ListComponent re-renders, all ListItem components will also re-render, even if their props haven’t changed.

Fix: Memoization

Wrap the ListItem component in React.memo to prevent unnecessary re-renders:

const ListItem = React.memo(({ item }) => {
  console.log("Rendering:", item.id);
  return <li>{item.name}</li>;
});
Enter fullscreen mode Exit fullscreen mode

Now, ListItem will only re-render when its item prop actually changes.


(b) New Array Instances on Every Render

If the list is derived from a prop or state that gets re-created on every render, React will treat it as a new list, even if the items haven't changed.

const App = () => {
  const items = [{ id: 1, name: "Item 1" }, { id: 2, name: "Item 2" }];
  return <ListComponent items={items} />;
};
Enter fullscreen mode Exit fullscreen mode

Even though the items array looks the same, it is a new array on every render.

Fix: Use useMemo

Memoizing the list prevents React from treating it as a new array:

const App = () => {
  const items = useMemo(() => [
    { id: 1, name: "Item 1" },
    { id: 2, name: "Item 2" }
  ], []);
  return <ListComponent items={items} />;
};
Enter fullscreen mode Exit fullscreen mode

Now, React will recognize items as the same reference across renders, preventing unnecessary list updates.


(c) Inline Function Props Causing Re-renders

If an inline function is passed as a prop to a list item, it creates a new function on every render, causing child components to re-render unnecessarily.

const ListComponent = ({ items }) => {
  return (
    <ul>
      {items.map((item) => (
        <ListItem key={item.id} item={item} onClick={() => console.log(item.id)} />
      ))}
    </ul>
  );
};
Enter fullscreen mode Exit fullscreen mode

Since a new onClick function is created on every render, ListItem will re-render even if item remains unchanged.

Fix: Use useCallback

Wrap the function in useCallback to maintain a stable reference:

const ListComponent = ({ items }) => {
  const handleClick = useCallback((id) => console.log(id), []);

  return (
    <ul>
      {items.map((item) => (
        <ListItem key={item.id} item={item} onClick={() => handleClick(item.id)} />
      ))}
    </ul>
  );
};
Enter fullscreen mode Exit fullscreen mode

Now, the onClick function reference remains the same, preventing unnecessary re-renders.


2. When Keyed Lists Do Need to Re-render

There are scenarios where React correctly re-renders list items:

  • Item properties change: If an item’s props change, React must re-render it.
  • List order changes: If items are re-ordered, React updates the DOM accordingly.
  • Items are added or removed: React efficiently updates only the affected items.

These behaviors are expected and correct—but unnecessary re-renders due to avoidable mistakes should be optimized.


3. Summary & Best Practices

Use React.memo to prevent re-renders of list items when props don’t change.
Use useMemo to avoid unnecessary re-creations of lists.
Use useCallback for event handlers to maintain stable function references.
Ensure keys are unique and stable to help React track list items correctly.

By following these best practices, you can drastically improve performance in your React applications by reducing unnecessary re-renders in keyed lists.

Top comments (0)