为什么 'key' 不传播到 {children} 元素映射中的反应元素?

Why does 'key' not propagate to react elements in map of {children} elements?

我正在实现一个组件,使用户可以更轻松地向页面添加多个项目。因为我有几种类型的项目可以由用户添加,所以我认为拥有一个带有“children”参数的组件是最有用的。

但是,在映射子元素时,实现在使用“key”参数时遇到问题。

function ExpandableSet({
   startingSet,
   itemTitle,
   children
})
{

   const largestId = useMemo(() => {
      return startingSet.reduce((largestId, item) =>
         if (item.id > largestId){
            return item.id
         }
         return largestId
      }, 0)
   }, [])

   const [itemCount, setItemCount] = useState(largestId + 1);
   const [items, setItems] = useState(startingSet);

   return(
      <div className="expandable-set">
         { items.map( item =>
            <div className="expanded-item" id={item['key']} key={item['key']}> { children } </div>
         )}
      </div>
      <button onClick={() =>
         {
            items.push({"key" : itemCount});
            setItemCount(itemCount + 1);
         }
      }>
         Add {itemTitle}
      </button>
   )
}

使用ExpandableSet时,是这样使用的:

...
<div className="container-for-expandable-set">
   <ExpandableSet
      startingSet={[]}
      itemTitle="Some item I want a lot of" />
      <SomeItem opts=opts />
   </ExpandableSet>
</div>
...

因为我不知道会有多少 SomeItem 元素,所以如果不管理父级的状态,就无法在父级传递密钥。这将破坏 ExpandableSet 组件的目的(也许这是正确的答案?)

输出时,我看到标准 each child in a list should have a unique 'key' prop 错误。

HTML输出

...
<div class="expandable-set">
 <div class="expanded-item" id="0">
  <div class="child-template-passed">
   ...
  </div>
 </div>
 <div class="expanded-item" id="1">
  <div class="child-template-passed">
   ...
  </div>
 </div>
</div>
...

为什么我仍然看到 key 错误?

您从一个可能有 N 个元素的起始集合开始,每个元素大概有从零开始的任何 ID,然后是一个从零开始的计数器。

因此,如果我通过页面上的按钮将一个项目添加到集合中,它的 ID 将为零。但是,如果起始集中的某个项目的 ID 已经为零怎么办?您现在在集合中有一个重复的 ID。

您需要找到起始集合中最大的ID,并将此ID加一作为itemCount的初始值。

const largestID = useMemo(() => {
  return startingSet.reduce((largestID, item) => {
    if (item.key > largestID) {
      return item.key
    }
    return largestID
  }, 0)
}, [startingSet])

const [items, setItems] = useState(startingSet);
const [itemCount, setItemCount] = useState(largestID + 1);