识别React关键属性的正确方法是什么?

zip*_*zit 3 reactjs react-native

我正在努力学习React-Native.我正在看一个Joshua Sierles的响应式图像网格示例.(谢谢Joshua!)在这个示例中,Joshua使用React以可控的方式在移动显示屏上小心地放置图像元素.注意:他只使用三张图像,并在文档中重复多次.不幸的是,如上所述,该示例生成警告:

警告:数组或迭代器中的每个子节点都应该具有唯一的"键"支柱.检查渲染方法YourProjectNameHere.有关更多信息,请参见 fb.me/react-warning-keys.(从缩短的形式推断出的链接......)

我完全理解一行中的每个元素和React生成的每一行必须具有唯一的键属性. 我不清楚的是如何做到这一点.这是我的黑客/解决方案. key={Math.random()}

这个hack工作正常,但它似乎是......错了.这里的问题是,识别单个图像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);
Run Code Online (Sandbox Code Playgroud)

我已经尝试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>
       )
     })
   },
Run Code Online (Sandbox Code Playgroud)

JMM*_*JMM 5

我认为,如果要移动组件(例如更改顺序)或预先添加元素,关键只是真正相关.如果这对您来说不是问题,而您只想使警告静音,则可以使用数组索引(第二个arg map()).

React Docs的简短官方解释在React Docs - Listing Mutations中.

扩大答案

我将扩展这一点,因为我相信你接受的另一个答案是误导性的:不是一个合理的解决方案,因为它呈现出来.

我的理解是,React将一个元素子元素解释为一个特殊情况,其中元素的长度或相对位置可能会在需要保留组件实例时发生变化.在React文档中,他们描述如下:

当孩子们被洗牌时(如在搜索结果中)或者如果新组件被添加到列表的前面(如在流中).

但是,通常使用数组只是为了方便脚本生成子项,否则可以按字面/静态表示.这就是我认为你在这里的情况.如果不是这种情况,您将需要并且可能已经具有合法的唯一ID.例如,如果我正确理解您的用例,您可以执行类似的操作,在这种情况下,React不会发出任何关键警告:

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>
Run Code Online (Sandbox Code Playgroud)

显然,这将是一种可怕的方式,因此使用数组.但是React并没有将这种数组的使用(与其等效的脚本)区别开来(其中元素的长度和顺序是动态的)并且抱怨关键.您可以忽略该警告,但只需指定元素的索引值即可轻松实现静音.

简化示例.这些变化产生相同的输出,但不同之处在于字面上声明或编写脚本并发出关键警告.

var container = document.getElementById('container');
var items = ["A", "B", "C"];
var groups = Array(2).fill(items);

function render (element) {
  ReactDOM.render(
    element,
    container
  );
}

console.log("literal, no key warning");

var element = <div>
  <div class="items">
    <div>{items[0]}</div>
    <div>{items[1]}</div>
    <div>{items[2]}</div>
  </div>

  <div class="items">
    <div>{items[0]}</div>
    <div>{items[1]}</div>
    <div>{items[2]}</div>
  </div>
</div>;

render(element);


console.log("scripted, no key warning");

var element = <div>
  {groups.map((items, i) =>
    <div class="items" key={i}>
      {items.map((item, j) =>
        <div key={j}>{item}</div>
      )}
    </div>
  )}
</div>;

render(element);


console.log("scripted, with key warning");

var element = <div>
  {groups.map((items, i) =>
    <div class="items">
      {items.map(item =>
        <div>{item}</div>
      )}
    </div>
  )}
</div>;

render(element);
Run Code Online (Sandbox Code Playgroud)

  • 好吧,我不认为其他答案是"正确的方式",但你已将其标记为已被接受. (2认同)