当我们对子节点进行递归的时候,没有给li元素设置key,当渲染到页面的时候就会报警告。
<ul>
{fruitsArr.map(item => {
return <li>{item}</li>
})}
</ul>
意思就是每一个在列表中的子孩子都必须有唯一的key。官网中写到“ key 不需要全局唯一,但在列表中需要保持唯一。”
当上面的代码中添加了key以后,页面就不会报警告了。
<ul>
{fruitsArr.map(item => {
return <li key={item}>{item}</li>
})}
</ul>
那么有些人,可能直接就用遍历时候的下标当做key值。官网中有一段话我们可以借鉴一下:
你也可以使用元素在数组中的下标作为key。这个策略在元素下不进行重新排序时比较合适,但一旦有顺序修改,diff就会变得慢。
当基于下标的组件进行重新排序时,组件state可能会遇到一些问题。由于组件实例是基于它们的key来决定是否更新以及复用,如果key是一个下标,那么修改顺序时会修改当前的key,导致非受控组件的state(比如输入框)可能相互篡改导致无法预期的变动。(官网有例子可以参考)
同时我们去看react的源码,packages > react-reconciler > src > ReactChildFiber.js > reconcileSingleElement以及deleteRemainingChildren的方法,就可以看出key作用
function reconcileSingleElement(
returnFiber: Fiber,
currentFirstChild: Fiber | null,
element: ReactElement,
lanes: Lanes,
): Fiber {
// 获取当前元素key值
const key = element.key;
let child = currentFirstChild;
while (child !== null) {
// TODO: If key === null and child.key === null, then this only applies to
// the first item in the list.
// currentFirstChild的key与当前元素key值进行比较,也就是说,先对key值做第一层比较,然后做下面的操作。
if (child.key === key) {
switch (child.tag) {
case Fragment: {
if (element.type === REACT_FRAGMENT_TYPE) {
deleteRemainingChildren(returnFiber, child.sibling);
const existing = useFiber(child, element.props.children);
existing.return = returnFiber;
if (__DEV__) {
existing._debugSource = element._source;
existing._debugOwner = element._owner;
}
return existing;
}
break;
}
case Block:
if (enableBlocksAPI) {
let type = element.type;
if (type.$$typeof === REACT_LAZY_TYPE) {
type = resolveLazyType(type);
}
if (type.$$typeof === REACT_BLOCK_TYPE) {
// The new Block might not be initialized yet. We need to initialize
// it in case initializing it turns out it would match.
if (
((type: any): BlockComponent<any, any>)._render ===
(child.type: BlockComponent<any, any>)._render
) {
deleteRemainingChildren(returnFiber, child.sibling);
const existing = useFiber(child, element.props);
existing.type = type;
existing.return = returnFiber;
if (__DEV__) {
existing._debugSource = element._source;
existing._debugOwner = element._owner;
}
return existing;
}
}
}
// We intentionally fallthrough here if enableBlocksAPI is not on.
// eslint-disable-next-lined no-fallthrough
default: {
if (
child.elementType === element.type ||
// Keep this check inline so it only runs on the false path:
(__DEV__
? isCompatibleFamilyForHotReloading(child, element)
: false)
) {
deleteRemainingChildren(returnFiber, child.sibling);
const existing = useFiber(child, element.props);
existing.ref = coerceRef(returnFiber, child, element);
existing.return = returnFiber;
if (__DEV__) {
existing._debugSource = element._source;
existing._debugOwner = element._owner;
}
return existing;
}
break;
}
}
// Didn't match.
deleteRemainingChildren(returnFiber, child);
break;
} else {
deleteChild(returnFiber, child);
}
child = child.sibling;
}
if (element.type === REACT_FRAGMENT_TYPE) {
const created = createFiberFromFragment(
element.props.children,
returnFiber.mode,
lanes,
element.key,
);
created.return = returnFiber;
return created;
} else {
const created = createFiberFromElement(element, returnFiber.mode, lanes);
created.ref = coerceRef(returnFiber, currentFirstChild, element);
created.return = returnFiber;
return created;
}
}
所以key的主要作用来比较标记同级节点上当前元素的唯一性。然后对他进行相应的增删改查,加快渲染速度。