Home > Back-end >  Each child in a list should have a unique "key", despite supplying unique keys
Each child in a list should have a unique "key", despite supplying unique keys

Time:11-07

My component renders as follows:

if (!units) return '';
return (
  <div className = "learn">  
    {render(rootId)}
  </div>
);

Here is render (note recursion):

const render = (id) => {
  const unit = units[id];
  return (
    <>
    <div key={id} className={id}>{unit.title}</div>
    {/* No warning without the following line */}
    {unit.children.map(childId => render(childId))} 
    </>
  );
}

All ids are unique. To make sure, I added a className and here is the resulting html:

enter image description here

Thus, it seems that all divs do have a unique key. Nonetheless, I get the warning Each child in a list should have a unique "key". What am I missing?

CodePudding user response:

React key should be on the outer-most mapped element, the React Fragment in this case.

const render = (id) => {
  const unit = units[id];
  return (
    <Fragment key={id}>
      <div className={id}>{unit.title}</div>
      {unit.children.map(childId => render(childId))} 
    </Fragment>
  );
}

If you are rendering only the div then the key could remain there.

const render = (id) => {
  const unit = units[id];
  return (
    <div key={id} className={id}>{unit.title}</div>
  );
}

To avoid mounting/remounting issues you should redefine render to be a component instead. (per @DennisVash)

const RenderUnits = ({ id }) => {
  const unit = units[id];
  return (
    <>
      <div className={id}>{unit.title}</div>
      {unit.children.map(id => (
        <RenderUnits key={id} id={id} /> // <-- React key here
      ))}
    </>
  );
};

...

if (!units) return '';
return (
  <div className = "learn">  
    <RenderUnits id={rootId} />
  </div>
);

CodePudding user response:

In this case even if you fix the warning, the keys are redundant since you remount the node on every render(...) call.

Possible fix example in addition to @Drew Reese answer:

// Don't have context from where units or id comes from,
// assumption: unit = units[id] from parent
const App = ({ unit }) => {
  if (!unit) return "";
  return (
    <div className="learn">
      <UnitsComponent units={unit} id={rootId} />
    </div>
  );
};

const UnitsComponent = ({ units, rootId }) => {
  return (
    <React.Fragment key={rootId}>
      <div className={id}>{unit.title}</div>
      {units.children.map((childId) => (
        <UnitsComponent key={rootId} rootId={rootId} />
      ))}
    </React.Fragment>
  );
};
  • Related