DEV Community

Cover image for What if: we have two same keys in Reactjs?
Hoang Duy
Hoang Duy

Posted on

What if: we have two same keys in Reactjs?

It is duythenights again! Great to have you back.

If you’ve seen that yellow warning in your console: "Encountered two children with the same key," don't ignore it.

This is not just a cosmetic console error; it is a fundamental logic failure that results in what I call "Reconciliation Chaos." To understand why this happens, we need to look at how React manages the relationship between your data and the physical DOM.

duplicate keys


The Core Problem: How React Logic Breaks

To see the disaster in action, let's look at exactly what happens step-by-step when you use duplicate keys.

The Setup:

Repository link: demo-two-same-keys
Demo link: https://demo-two-same-keys-in-reactjs.vercel.app/
Suppose you have an array of 4 items: A, B, C, D.

  • Item A: ID = "1"
  • Item B: ID = "1" (Duplicate!)
  • Item C: ID = "3"
  • Item D: ID = "4"

demo duplicated keys

  const [items, setItems] = useState([
    { id: "1", text: "Item A" },
    { id: "1", text: "Item B" }, // Duplicate ID
    { id: "3", text: "Item C" },
    { id: "4", text: "Item D" },
  ]);

  const removeA = () => {
    setItems(items.filter((item) => item.text !== "Item A"));
  };

  const idCount: Record<string, number> = {};
  items.forEach((item) => {
    idCount[item.id] = (idCount[item.id] || 0) + 1;
  });
Enter fullscreen mode Exit fullscreen mode
 <ul className="item-list">
          {items.map((item) => {
            const isDuplicate = idCount[item.id] > 1;
            return (
              <li
                key={item.id}
                className={`item-row${isDuplicate ? " duplicate" : ""}`}
              >
                <span className="item-id">{item.id}</span>
                <span className="item-text">{item.text}</span>
                <input className="item-input" placeholder="Type here…"    />
                {isDuplicate && (
                  <span className="badge-duplicate">dup</span>
                )}
              </li>
            );
          })}
</ul>
Enter fullscreen mode Exit fullscreen mode

The Action:

You type some text in A and B

You type some text in A and B
You click a button to delete Item A.

You click a button to delete Item A

Result: title B with input content A

title B with input content A

The Step-by-Step Breakdown:

  • Step 1 (Data Update): Your code runs a filter through the array. The data is updated correctly. The new array now contains only B, C, D.
  • Step 2 (Virtual DOM Check): React’s Virtual DOM checks the IDs needed for the new render. It sees it needs to render ID "1" (Item B), ID "3" (Item C), and ID "4" (Item D).
  • Step 3 (The Wrong Reuse): React looks at the existing physical DOM. It sees a DOM node with ID=1 already exists at the top. Because React is "lazy" to save performance, it says: "Since ID=1 is already in the DOM and still in my data, I will just reuse it." But this is actually Item A's DOM node. React then assumes the actual DOM node for Item B is a redundant duplicate and deletes it. Items C and D remain untouched.
  • Step 4 (Data Injection): Now, React loads the new data into the existing DOM nodes. It takes the text "B" from your array and sets it as the innerText for Item A's old DOM node.

Reconciliation Chaos: The Ghost and the Shell

This process creates a "Frankenstein" component where the "Shell" (The DOM) belongs to one item, but the "Ghost" (The Data) belongs to another.

1. The Shell (The Physical DOM)

Because the physical DOM node of Item A was never destroyed, it keeps everything that React doesn't explicitly control through props. This includes Internal States such as:

  • Uncontrolled Input values: The text a user typed into a box.
  • Focus state: Which element the cursor is currently in.
  • Scroll position: How far down a specific div has been scrolled.
  • CSS Animations: A transition that was halfway finished.

2. The Ghost (The Injected Data)

React pours Item B’s data into Item A’s old "Shell." The labels update, the images change, but the "Shell" is still haunted by Item A's state.

The Result: You see the name "Item B" on the screen, but if you had typed "Hello A" into an input field, that text is still there. The UI has lied to the user because the identity was not unique.

3. The Performance Collapse (Re-render Panic)

Beyond just visual bugs, duplicate keys act as a "performance killer" for your application. React's speed relies on Surgical Updates—the ability to change only the tiny piece of the DOM that actually changed. When keys are duplicated, this surgical precision is destroyed.

  • Breaking Memoization: If you are using React.memo or PureComponent to optimize your list, duplicate keys make them useless. React cannot reliably compare the previous props with the new props because it can't verify which data belongs to which instance.
  • The "Panic" Remount: Because React is confused about the identity of your items, it often gives up on "updating" and resorts to "remounting." Instead of just changing a text label, React may destroy the entire DOM subtree and recreate it from scratch.
  • The Domino Re-render: In a healthy list, deleting one item should only trigger a change at that specific index. With duplicate keys, a single change can trigger a "Domino Effect," forcing React to re-evaluate and re-render every single item in the list because it has lost the "map" of your UI.

How to Stay Safe

If your data source returns duplicate IDs, follow these steps:

  1. Uniqueness is Law: Treat the key prop like a Primary Key in a database. It must be unique among siblings.
  2. Sanitize Data: If your API returns duplicate IDs, fix them before they reach your state.
  3. Stable Identity: Never use the array index as a key if the list can be filtered or deleted.

The Bottom Line: React’s speed comes from its ability to reuse DOM nodes. If you provide duplicate keys, React reuses the wrong nodes, resulting in corrupted UI states that are a nightmare to debug.

Top comments (0)