2016-04-23 2 views
3

Я пытаюсь изучить React-Native. Я смотрю на an example, Responsive image grid by Joshua Sierles. (Спасибо, Джошуа!). В этом примере Джошуа использует React для тщательного размещения элементов изображения контролируемым образом на мобильном дисплее. Примечание: он использует ТОЛЬКО три изображения и повторяет их несколько раз в документе. К сожалению, как написано, пример генерирует предупреждение:Каков правильный способ идентификации свойств ключа React?

Предупреждение: Каждый ребенок в массиве или итератора должен иметь уникальный «ключ» опору. Проверьте метод рендеринга YourProjectNameHere. См. fb.me/react-warning-keys для получения дополнительной информации. (Ссылка экстраполированы из укороченной формы ...)

Я полностью понимаю, что каждый элемент в строке, и каждая строка генерируется Реагировать must have a unique key property. То, что я не ясно, как именно это сделать. Вот мой хак/работа вокруг. key={Math.random()}

Этот хак отлично работает, но это просто кажется ... неправильным. Вопрос в том, как правильно идентифицировать идентификаторы отдельных изображений, а также идентифицировать отдельные идентификаторы строк?

'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, что я могу думать. Ничего не работает так же хорошо, как функция Random Number. Другие идеи? Каков правильный способ сделать это?


Обновление для ответа Криса Гейрмана ниже: Я хотел показать свой последний код.

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> 
     ) 
    }) 
    }, 

ответ

4

Я думаю, что ключ действительно имеет значение, если вы собираетесь перемещать компоненты вокруг (например, изменять порядок) или добавлять элементы. Если это не проблема для вас, и вы просто хотите отключить предупреждение, вы можете использовать индекс массива (второй arg - map()).

Краткое официальное объяснение алгоритма согласования реакций находится в React Docs - Listing Mutations.

развернутый ответ

Я собираюсь остановиться на этом, потому что я считаю, что другой ответ, что вы приняли, вводит в заблуждение: не законны решение, так как он представляет себя.

Мое понимание заключается в том, что React интерпретирует дочерний элемент, который является массивом как особый случай, когда длина или относительное положение элементов могут меняться, в то время как экземпляры компонентов должны быть сохранены. В React docs они описывают это так:

когда дети перемешиваются вокруг (как и в результатах поиска), или, если новые компоненты добавляются на передней части списка (как в потоках).

Но часто массив используется только для удобства генерации скриптов для детей, которые в противном случае могли бы быть выражены буквально/статически. И это так, я думаю, у вас здесь. Если это не так, вам понадобится и, скорее всего, уже имеют законные уникальные идентификаторы.Например, если я правильно понял ваш случай использования правильно, вы можете просто сделать что-то вроде этого, и в этом случае не было бы никакого ключа предупреждения от 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> 

Очевидно, что это было бы ужасающим способом сделать это, поэтому использование массивов. Но React не различает это использование массива (чтобы сценарий эквивалент этого) из другого случая (где длина и порядок элементов являются динамическими) и жалуется на ключ. Вы можете просто игнорировать предупреждение, но его легко отключить, просто назначив значение индекса для элемента.

Simplified example. Эти вариации производят эквивалентную продукцию, но отличаются тем, что они буквально объявлены или написаны сценарием и выдают предупреждение о ключе или нет.

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); 
+0

правильный ответ, но я действительно хочу, чтобы узнать, как сделать это правильно в будущем (и понять, почему это правильно сделать. ..) – zipzit

+0

Тогда имейте в виду, что A) другой ответ фактически не использует уникальный ключ для каждого компонента, он все еще использует индекс. Если эти компоненты были с точки зрения состояния или что-то еще, и вы изменили относительные позиции некоторых с тем же URI, это не сработало. Если в этом случае количество элементов и их положение внутри массива не изменятся в течение жизни страницы, то индекса будет достаточно. Если эти вещи будут меняться, и вам действительно нужно однозначно идентифицировать компоненты, тогда вам понадобятся действительно уникальные идентификаторы. __ \\ _ (ツ) _/¯ B) Не обращается к компонентам ''. – JMM

+0

Вы правы. Я действительно признаю, что это надуманный пример. В действительности, если вы когда-либо использовали этот дизайн в приложении, у вас были бы уникальные образы во всем мире, и вам нужно было «щелкнуть» их и сделать что-то. Вы, ребята, заставили меня лучше понять, почему Реакт нуждается в уникальном идентификаторе. Много thx. – zipzit

0

«Ключ» - это то, как отреагирует отрезок реагирования, изменил ли объект, назначенный этому узлу, или нет. Это тактика оптимизации. Если один и тот же ключ назначается определенному узлу, этот рендер, которому был назначен последний рендер, тогда он не беспокоит повторную визуализацию. Таким образом, вы хотите, чтобы каждый ключ был уникальным для этого узла, но также был предсказуемым/последовательным и, следовательно, угаданным, так что каждый раз, когда вы визуализируете этот конкретный узел, ему будет присвоен тот же уникальный ключ.

Math.random() - это плохой выбор для ключа, поскольку он не соответствует предсказуемым/согласованным критериям. Использование индекса массива само по себе также является плохим выбором по аналогичным причинам.

Вместо этого, я бы рекомендовал использовать значение массива + индекс

renderRow(images) { 
    return images.map((uri, idx, arr) => { 
     return (
     <Image style={[styles.image, this.calculatedSize()]} key={uri.concat(idx)} source={{uri: uri}} /> 
     ) 
    }) 
    }, 
+0

Я не понял, почему вы добавили добавочное имя переменной 'arr' в строку кода' return images.map ((uri, idx, arr) => {... ', поэтому я просто его исключил. попытка попробовать что-то еще или конкретную точку обучения? Кажется, что все работает нормально, и да, я знал, что случайная вещь была плохая, просто плохая. Я обновлю свой вопрос, чтобы показать исправленный код за этот ответ. Thx! – zipzit

+0

Он просто использовал полную подпись метода для 'map'. – Interrobang

+0

это правильно, как указал @interrobang, я просто использовал полную подпись. Вы можете спокойно опустить ее, если вы выберете. –

Смежные вопросы