Ostrzeżenie: każde dziecko w tablicy lub iteratorze powinno mieć unikalną właściwość „klucz”. Sprawdź metodę renderowania „ListView”

105

Zbudowałem aplikację z ReactNative zarówno na iOS, jak i Androida z rozszerzeniemListView . Podczas wypełniania widoku listy prawidłowym źródłem danych na dole ekranu jest drukowane następujące ostrzeżenie:

Ostrzeżenie: każde dziecko w tablicy lub iteratorze powinno mieć unikalną właściwość „key”. Sprawdź metodę renderowania ListView.

Jaki jest cel tego ostrzeżenia? Po wiadomości odsyłają do tej strony , na której omawiane są różne rzeczy, które nie mają nic wspólnego z reagowaniem natywnym, ale z reakcjami internetowymi.

Mój ListView jest zbudowany z tych instrukcji:

render() {
    var store = this.props.store;

    return (

        <ListView
            dataSource={this.state.dataSource}
            renderHeader={this.renderHeader.bind(this)}
            renderRow={this.renderDetailItem.bind(this)}
            renderSeparator={this.renderSeparator.bind(this)}
            style={styles.listView}
            />

    );
}

Moje źródło danych składa się z czegoś takiego:

    var detailItems = [];

    detailItems.push( new DetailItem('plain', store.address) );
    detailItems.push( new DetailItem('map', '') );

    if(store.telefon) {
        detailItems.push( new DetailItem('contact', store.telefon, 'Anrufen', 'fontawesome|phone') );
    }
    if(store.email) {
        detailItems.push( new DetailItem('contact', store.email, 'Email', 'fontawesome|envelope') );
    }
    detailItems.push( new DetailItem('moreInfo', '') );

    this.setState({
        dataSource: this.state.dataSource.cloneWithRows(detailItems)
    });

A ListView-Rows są renderowane za pomocą rzeczy takich jak:

        return (
            <TouchableHighlight underlayColor='#dddddd'>
                <View style={styles.infoRow}>
                    <Icon
                                name={item.icon}
                                size={30}
                                color='gray'
                                style={styles.contactIcon}
                                />
                    <View style={{ flex: 1}}>
                        <Text style={styles.headline}>{item.headline}</Text>
                        <Text style={styles.details}>{item.text}</Text>
                    </View>
                    <View style={styles.separator}/>
                </View>
            </TouchableHighlight>
        );

Wszystko działa dobrze i zgodnie z oczekiwaniami, z wyjątkiem ostrzeżenia, które wydaje mi się kompletnym nonsensem.

Dodanie właściwości klucza do mojej klasy „DetailItem” nie rozwiązało problemu.

Oto co tak naprawdę zostanie przekazane do ListView w wyniku „cloneWithRows”:

_dataBlob: 
I/ReactNativeJS( 1293):    { s1: 
I/ReactNativeJS( 1293):       [ { key: 2,
I/ReactNativeJS( 1293):           type: 'plain',
I/ReactNativeJS( 1293):           text: 'xxxxxxxxxx',
I/ReactNativeJS( 1293):           headline: '',
I/ReactNativeJS( 1293):           icon: '' },
I/ReactNativeJS( 1293):         { key: 3, type: 'map', text: '', headline: '', icon: '' },
I/ReactNativeJS( 1293):         { key: 4,
I/ReactNativeJS( 1293):           type: 'contact',
I/ReactNativeJS( 1293):           text: '(xxxx) yyyyyy',
I/ReactNativeJS( 1293):           headline: 'Anrufen',
I/ReactNativeJS( 1293):           icon: 'fontawesome|phone' },
I/ReactNativeJS( 1293):         { key: 5,
I/ReactNativeJS( 1293):           type: 'contact',
I/ReactNativeJS( 1293):           text: '[email protected]',
I/ReactNativeJS( 1293):           headline: 'Email',
I/ReactNativeJS( 1293):           icon: 'fontawesome|envelope' },
I/ReactNativeJS( 1293):         { key: 6, type: 'moreInfo', text: '', headline: '', icon: '' } ] },

Jak widać z jednego klucza, każdy rekord ma właściwość klucza. Ostrzeżenie nadal istnieje.

usunąć
źródło
1
Najprawdopodobniej DetailItempotrzebujesz kluczy. Jeśli mają już unikalne klucze, musisz pokazać inne metody renderowania ( renderHeader, renderDetailItem, renderSeparator). Działają dobrze i są oczekiwane, dopóki źródło danych nie zostanie w jakiś sposób zmodyfikowane (na przykład wiersze zostaną usunięte), po czym React nie będzie wiedział, co z nimi zrobić, jeśli nie mają unikalnego identyfikatora.
Pete TNT
Co masz na myśli mówiąc „klucze”? Właściwość o nazwie „klucz”?
usuń
Tak. Zobacz facebook.github.io/react/docs/…
Pete TNT
To go nie rozwiązuje. Dodałem kluczową właściwość do mojej struktury danych i zaktualizowałem pierwotne pytanie o bardziej szczegółowe dane. Wyświetlanie zwykłych danych, których wynikiem jest źródło danych, ma klucz dla każdego rekordu. To ostrzeżenie pozostaje.
usuń
Może pochodzić również z innych metod renderowania (renderHeader, renderDetailItem, renderSeparator)
Pete TNT

Odpowiedzi:

95

Od jakiegoś czasu mam dokładnie ten sam problem, co Ty i po przejrzeniu niektórych z powyższych sugestii w końcu go rozwiązałem.

Okazuje się (przynajmniej dla mnie), że potrzebowałem dostarczyć klucz (rekwizyt o nazwie „klucz”) do komponentu, który zwracam z mojej metody renderSeparator. Dodanie klucza do mojego renderRow lub renderSectionHeader nic nie dało, ale dodanie go do renderSeparator sprawiło, że ostrzeżenie zniknęło.

Mam nadzieję, że to pomoże.

coldbuffet
źródło
W moim przypadku właśnie usunąłem renderSeparator i przeniosłem mój <Separator> do treści zwracanej wartości renderRow.
boatcoder
To samo dotyczy SectionList, trzeba jawnie dodać właściwość z kluczem nazwy dla każdego, itemaby uszczęśliwić RN.
LeOn - Han Li
1
Przed przeczytaniem tego straciłem około 8 godzin na śledzenie tego, co uważałem za problem z moimi danymi JSON. Gdyby stos się przepełnił: taco: dam ci jeden!
hoekma
79

Musisz podać klucz .

Spróbuj zrobić to w swoich ListView Rows, jeśli masz właściwość klucza:

<TouchableHighlight key={item.key} underlayColor='#dddddd'>

Jeśli nie, spróbuj po prostu dodać element jako klucz:

<TouchableHighlight key={item} underlayColor='#dddddd'>
Nader Dabit
źródło
1
bardziej podoba mi się ta odpowiedź, ponieważ ma kod, dzięki czemu mogę skopiować XD
Jovylle Bermudez
35

Możesz również użyć liczby iteracji (i) jako key:

render() {
    return (
      <ol>
        {this.props.results.map((result, i) => (
          <li key={i}>{result.text}</li>
        ))}
      </ol>
    );
}
Agu Dondo
źródło
2
Może to nie działać, gdy zmienia się tablica. Ta odpowiedź wyjaśnia to na przykładzie: stackoverflow.com/a/43892905/960857
Chris
22

Zmień kod z:

render() {
    return (
      <ol>
        {this.props.results.map((result) => (
          <li>{result.text}</li>
        ))}
      </ol>
    );
}

Do:

render() {
    return (
      <ol>
        {this.props.results.map((result) => (
          <li key={result.id}>{result.text}</li>
        ))}
      </ol>
    );
}

Następnie rozwiązany.

micsay
źródło
13

Dodaj atrybut „klucz” do głównego składnika renderowania listy.

<ScrollView>
      <List>
          {this.state.nationalities.map((prop, key) => {
             return (
               <ListItem key={key}>
                  <Text>{prop.name}</Text>
               </ListItem>
             );
          })}
      </List>
</ScrollView>
Shujat Munawar
źródło
6

To ostrzeżenie pojawia się, gdy nie dodajesz klucza do elementów listy. Zgodnie z odpowiedzią js Docs -

Klawisze pomagają Reagować zidentyfikować, które elementy uległy zmianie, zostały dodane lub usunięte. Klucze należy nadać elementom wewnątrz tablicy, aby nadać elementom stabilną tożsamość:

const numbers = [1, 2, 3, 4, 5];
const listItems = numbers.map((number) =>
  <li key={number.toString()}>
    {number}
  </li>
);

Najlepszym sposobem na wybranie klucza jest użycie łańcucha, który jednoznacznie identyfikuje element listy wśród jego rodzeństwa. Najczęściej jako kluczy używałbyś identyfikatorów ze swoich danych:

const todoItems = todos.map((todo) =>
  <li key={todo.id}>
    {todo.text}
  </li>
);

Jeśli nie masz stabilnych identyfikatorów renderowanych elementów, możesz w ostateczności użyć indeksu elementu jako klucza

const todoItems = todos.map((todo, index) =>
  // Only do this if items have no stable IDs
  <li key={index}>
    {todo.text}
  </li>
);
Jagrati
źródło
4

Naprawiłem to, dodając właściwość do renderSeparator Component, kod jest tutaj:

_renderSeparator(sectionID,rowID){
    return (
        <View style={styles.separatorLine} key={"sectionID_"+sectionID+"_rowID_"+rowID}></View>
    );
}

Słowa kluczowe tego ostrzeżenia to „unikalne”, identyfikator sekcji + identyfikator wiersza zwracają unikalną wartość w ListView.

bocai
źródło
4

Sprawdź: klucz = undef !!!

Otrzymałeś również ostrzeżenie:

Each child in a list should have a unique "key" prop.

jeśli twój kod jest kompletny poprawnie, ale jeśli jest włączony

<MyComponent key={someValue} />

someValue jest niezdefiniowana !!! Sprawdź to najpierw. Możesz zaoszczędzić godziny.

Gerd
źródło
3

Zakładając, że metoda renderDetailItem ma następujący podpis ...

(rowData, sectionID, rowID, highlightRow) 

Spróbuj to zrobić ...

<TouchableHighlight key={rowID} underlayColor='#dddddd'>
niebezpieczny
źródło
3

Konkretny kod, którego użyłem, aby to naprawić, to:

  renderSeparator(sectionID, rowID, adjacentRowHighlighted) {
    return (
      <View style={styles.separator} key={`${sectionID}-${rowID}`}/>
    )
  }

Dołączam konkretny kod, ponieważ potrzebujesz unikalnych kluczy - nawet w przypadku separatorów. Jeśli zrobisz coś podobnego, np. Jeśli ustawisz to na stałą, otrzymasz kolejny irytujący błąd dotyczący ponownego użycia kluczy. Jeśli nie znasz JSX, skonstruowanie wywołania zwrotnego do JS w celu wykonania różnych części może być dość uciążliwe.

I na ListView, oczywiście dołączając to:

<ListView
  style={styles.listview}
  dataSource={this.state.dataSource}
  renderRow={this.renderRow.bind(this)}
  renderSeparator={this.renderSeparator.bind(this)}
  renderSectionHeader={this.renderSectionHeader.bind(this)}/>

Podziękowania dla coldbuffeta i Nadera Dabita, którzy wskazali mi tę ścieżkę.

Selly
źródło
2

Tutaj jest oparte na moim zrozumieniu. Mam nadzieję, że to pomocne. Ma wyrenderować listę komponentów jako przykład. Znacznik główny każdego składnika musi mieć rozszerzenie key. Nie musi być wyjątkowy. Nie może być key=0, key='0'itd. Wygląda na to, że klucz jest bezużyteczny.

render() {
    return [
        (<div key={0}> div 0</div>),
        (<div key={1}> div 2</div>),
        (<table key={2}><tbody><tr><td> table </td></tr></tbody></table>),
        (<form key={3}> form </form>),
    ];
}
caot
źródło
0

Wydaje się, że oba warunki są spełnione, być może kluczowy („kontakt”) jest problemem

 if(store.telefon) {
    detailItems.push( new DetailItem('contact', store.telefon, 'Anrufen', 'fontawesome|phone') );
}
if(store.email) {
    detailItems.push( new DetailItem('contact', store.email, 'Email', 'fontawesome|envelope') );
}
Shiva Acharjee
źródło
nie, kontakt nie jest kluczem. W międzyczasie dodałem właściwość o nazwie „klucz” do mojej struktury danych i zaktualizowałem pytanie, podając bardziej szczegółowe dane. Nic nie pomaga. Ostrzeżenie pozostaje.
usuń
0

To, co mnie zaskoczyło, polegało na tym, że pomyślałem, że potrzeba klucza zastosowanego do czegoś, co wygląda jak „rzeczywiste” lub elementy HTML DOM, w przeciwieństwie do elementów JSX, które zdefiniowałem.

Oczywiście z Reactem pracujemy z wirtualnym DOM, więc zdefiniowane przez nas elementy React JSX <MyElement>są dla niego tak samo ważne, jak elementy, które wyglądają jak prawdziwe elementy HTML DOM <div>.

Czy to ma sens?

Ross Attrill
źródło
0

W moim przypadku korzystałem z widoku „Karta” reakcji semantycznej UI. Po dodaniu klucza do każdej skonstruowanej karty ostrzeżenie zniknęło, na przykład:

return (
        <Card fluid key={'message-results-card'}>
          ...
        </Card>
)
Michael Bordash
źródło
-1

Jeśli używasz <Fade in>elementu dla aplikacji Reaguj, dodaj key={}do niego również atrybut lub zobaczysz błąd w konsoli.

Reid McCulloch
źródło