我正在尝试学习React-Native。我正在看an example, Responsive image grid by Joshua Sierles.(谢谢Joshua!)在这个示例中,Joshua使用React以可控的方式在移动显示屏上小心地放置图像元素。注意:他只使用三张图像,并在文档中重复多次。不幸的是,如上所述,该示例会生成警告:
警告:数组或迭代器中的每个子节点都应该有一个唯一的"键" 支柱。检查
YourProjectNameHere
的呈现方法。看到 fb.me/react-warning-keys了解更多信息。 (从缩短的形式推断的链接......)
我完全理解React must have a unique key property.生成的行和每一行中的每个元素我不清楚的是如何做到这一点。这是我的黑客/解决方法。 key={Math.random()}
这个黑客工作正常,但它似乎是......错了。这里的问题是,识别单个图像ID的正确方法是什么,以及识别单个行ID?
'use strict';
var React = require('react-native');
var {
AppRegistry,
StyleSheet,
Text,
View,
Image,
Dimensions,
ScrollView
} = React;
var _ = require('lodash');
var {width, height} = Dimensions.get('window');
var IMAGE_URLS = _.flatten(_.times(9, () => {return ['http://rnplay.org/IMG_0599.jpg', 'http://rnplay.org/IMG_0602.jpg', 'http://rnplay.org/IMG_0620.jpg']})); // 9 x 3 = 27 images
var IMAGES_PER_ROW = 4;
var AwesomeProject1 = React.createClass({
getInitialState() {
return {
currentScreenWidth: width,
currentScreenHeight: height
}
},
handleRotation(event) {
var layout = event.nativeEvent.layout
this.setState({currentScreenWidth: layout.width, currentScreenHeight: layout.height })
},
calculatedSize() {
var size = this.state.currentScreenWidth / IMAGES_PER_ROW
return {width: size, height: size}
},
// note: I added key={Math.random()} in two places below.
// Its a BS fix, but it seems to work to avoid the warning message.
renderRow(images) {
return images.map((uri) => {
return (
<Image style={[styles.image, this.calculatedSize()]} key={Math.random()} source={{uri: uri}} /> //key={Math.random()}
)
})
},
renderImagesInGroupsOf(count) {
return _.chunk(IMAGE_URLS, IMAGES_PER_ROW).map((imagesForRow) => {
return (
<View style={styles.row} key={Math.random()}>
{this.renderRow(imagesForRow)}
</View>
)
})
},
render: function() {
return (
<ScrollView onLayout={this.handleRotation} contentContainerStyle={styles.scrollView}>
{this.renderImagesInGroupsOf(IMAGES_PER_ROW)}
</ScrollView>
);
}
});
var styles = StyleSheet.create({
row: {
flexDirection: 'row',
alignItems: 'center',
justifyContent: 'flex-start'
},
image: {
}
});
AppRegistry.registerComponent('AwesomeProject1', () => AwesomeProject1);
我已经尝试了key=uri.id , imagesForRow.id, images.id, etc
的每一个组合,我能想到的。无效与随机数功能一样好。其他想法?什么是正确的方法?
根据Chris Geirman的回复,下面给出了答案:我想展示我的最终代码。
renderRow(images) {
return images.map((uri, idx) => {
return (
<Image style={[styles.image, this.calculatedSize()]} key={uri.concat(idx)} source={{uri: uri}} /> //key={Math.random()}
)
})
},
renderImagesInGroupsOf(count) {
return _.chunk(IMAGE_URLS, IMAGES_PER_ROW).map((imagesForRow, idx2) => {
return (
<View style={styles.row} key={imagesForRow.concat(idx2)}>
{this.renderRow(imagesForRow)}
</View>
)
})
},
答案 0 :(得分:5)
我认为只有当你要移动组件(例如更改顺序)或前置元素时,关键才真正相关。如果这对您来说不是问题,而您只想使警告静音,则可以使用数组索引(第二个arg到free_result()
)。
React对帐算法的简短官方说明在React Docs - Listing Mutations。
我将对此进行扩展,因为我相信你所接受的另一个答案是误导性的:不是一个合理的解决方案,因为它呈现出来。
我的理解是,React将一个元素子元素解释为一个特殊情况,其中元素的长度或相对位置可能会在需要保留组件实例时发生变化。在React docs中,他们将其描述为:
当孩子们被洗牌时(如在搜索结果中)或者新组件被添加到列表的前面(如在流中)。
但是,通常使用数组只是为了方便脚本生成子项,否则可以按字面/静态表示。这就是我认为你在这里的情况。如果不是这种情况,您将需要并且可能已经具有合法的唯一ID。例如,如果我正确理解您的用例,您可以执行类似的操作,在这种情况下,React不会发出任何关键警告:
map()
显然,这将是一种可怕的方式,因此使用数组。但是React并没有将这种数组的使用(与其等效的脚本)区别开来(其中元素的长度和顺序是动态的)并且抱怨关键。你可以忽略警告,但只需指定元素的索引值就可以轻松实现静音。
Simplified example。这些变化产生相同的输出,但不同之处在于字面上声明或编写脚本并发出关键警告。
var image_urls = [
'http://rnplay.org/IMG_0599.jpg',
'http://rnplay.org/IMG_0602.jpg',
'http://rnplay.org/IMG_0620.jpg'
];
{/* #1 */}
<View>
<Image style={[styles.image, this.calculatedSize()]} source={{uri: image_urls[0]}} />
<Image style={[styles.image, this.calculatedSize()]} source={{uri: image_urls[1]}} />
<Image style={[styles.image, this.calculatedSize()]} source={{uri: image_urls[2]}} />
<Image style={[styles.image, this.calculatedSize()]} source={{uri: image_urls[0]}} />
</View>
{/* ... */
{/* #n */}
<View>
<Image style={[styles.image, this.calculatedSize()]} source={{uri: image_urls[0]}} />
<Image style={[styles.image, this.calculatedSize()]} source={{uri: image_urls[1]}} />
<Image style={[styles.image, this.calculatedSize()]} source={{uri: image_urls[2]}} />
<Image style={[styles.image, this.calculatedSize()]} source={{uri: image_urls[0]}} />
</View>
答案 1 :(得分:0)
&#34; key&#34;反应的反应是如何确定分配给该节点的对象是否已更改。这是一种优化策略。如果为特定节点分配了相同的密钥,则该渲染为最后一次渲染的渲染,那么它就不会重新渲染。因此,您希望每个键对于该节点都是唯一的,但也是可预测/一致的,因此可以猜测,这样每次渲染该特定节点时,都会为其分配完全相同的唯一键。
Math.random()是密钥的不良选择,因为它无法通过可预测/一致的标准。出于类似的原因,使用数组索引本身也是一个糟糕的选择。
相反,我建议使用数组值+索引
renderRow(images) {
return images.map((uri, idx, arr) => {
return (
<Image style={[styles.image, this.calculatedSize()]} key={uri.concat(idx)} source={{uri: uri}} />
)
})
},