React.Children.map中的React.cloneElement正在导致元素键发生更改
正如标题状态,使用React.cloneElement
内React.Children.map
导致元素键来改变。
这是一个演示这个的沙箱。
React.Children.map(children, (child) => {
let clonedEl = React.cloneElement( child );
console.log(clonedEl);
return clonedEl;
});
该代码块的结果具有添加到每个键前面的.$
元素。 这真是令人困惑,有两个原因。
1:文档说cloneElement将保留键和参考。
使用元素克隆并返回一个新的React元素作为起点。 由此产生的元素将具有原始元素的道具,新道具融合得很浅。 新的孩子将取代现有的孩子。 键和来自原始元素的参考将被保留。
2: console.log
的结果是一个保存有键和ref的元素。
这会让我相信这个添加发生在React.Children.map代码的某处。
更新:在查看React.Children.map的代码后...
我想通过下面的函数链添加它:mapChilren - > mapIntoWithKeyPrefixInternal - > traverseAllChildren - > traverseAllChildrenImpl - > mapSingleChildIntoContext。
mapSingleChildIntoContext
的第三个参数是childKey。 它被称为nameSoFar === '' ? SEPARATOR + getComponentKey(children, 0) : nameSoFar
nameSoFar === '' ? SEPARATOR + getComponentKey(children, 0) : nameSoFar
因为它是traverseAllChildrenImpl
的第三个参数。
SEPARATOR = "."
并且getComponentKey
在转义函数中返回带有$前缀的键。
更新的问题:
现在我正在寻找解决此问题的方法...我不确定是否有人考虑traverseAllChildrenImpl在traverseAllChildren中使用空字符串作为nameSoFar
。
我认为这可能意味着React.Children.map
构建新的DOM的预期行为。 当试图更新动态儿童道具时,这对我造成了一定的影响。
解决方案:不要使用他们不打算使用的东西。
我正在构建一组对开发人员非常简单的表单控件。 状态树是通过映射孩子并使用动态构建的。 从具有名称的元素中划定字符串名称以在顶层组件上创建键和值。
顶层表单组件具有onChange处理程序,用于处理不同类型的控件,并根据需要应用于元素的onChange属性。 这个映射是在componentWillMount方法中完成的,是什么导致了我的问题。
将映射移动到渲染方法允许我不必更新句柄中的子项。 手柄更新导致元素失去焦点。 现在一切都很好!
问题不在于更改密钥的cloneElement
。 正如文档中所写,cloneElement保留原始密钥。 它的React.Children.map
它添加一个前缀。 如果您不希望密钥更改,请使用forEach
而不是map
这是来自React Code的摘录:
function escape(key) {
var escapeRegex = /[=:]/g;
var escaperLookup = {
'=': '=0',
':': '=2',
};
var escapedString = ('' + key).replace(escapeRegex, function(match) {
return escaperLookup[match];
});
return '$' + escapedString;
}
function getComponentKey(component, index) {
// Do some typechecking here since we call this blindly. We want to ensure
// that we don't block potential future ES APIs.
if (
typeof component === 'object' &&
component !== null &&
component.key != null
) {
// Explicit key
return escape(component.key);
}
// Implicit key determined by the index in the set
return index.toString(36);
}
function mapSingleChildIntoContext(bookKeeping, child, childKey) {
var {result, keyPrefix, func, context} = bookKeeping;
var mappedChild = func.call(context, child, bookKeeping.count++);
if (Array.isArray(mappedChild)) {
mapIntoWithKeyPrefixInternal(
mappedChild,
result,
childKey,
emptyFunction.thatReturnsArgument,
);
} else if (mappedChild != null) {
if (ReactElement.isValidElement(mappedChild)) {
mappedChild = ReactElement.cloneAndReplaceKey(
mappedChild,
// Keep both the (mapped) and old keys if they differ, just as
// traverseAllChildren used to do for objects as children
keyPrefix +
(mappedChild.key && (!child || child.key !== mappedChild.key)
? escapeUserProvidedKey(mappedChild.key) + '/'
: '') +
childKey,
);
}
result.push(mappedChild);
}
}
function mapChildren(children, func, context) {
if (children == null) {
return children;
}
var result = [];
mapIntoWithKeyPrefixInternal(children, result, null, func, context);
return result;
}
链接地址: http://www.djcxy.com/p/27565.html
上一篇: React.cloneElement inside React.Children.map is causing element keys to change
下一篇: Using jQuery .attr or .prop to set attribute value not working