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:
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>
);
};