Cảnh báo: Mỗi phần tử con trong một mảng hoặc trình vòng lặp phải có một chỗ dựa “khóa” duy nhất. Kiểm tra phương thức kết xuất của `ListView`


102

Tôi đã xây dựng một ứng dụng với ReactNative cho cả iOS và Android với a ListView. Khi điền vào listview với một nguồn dữ liệu hợp lệ, cảnh báo sau được in ở cuối màn hình:

Cảnh báo: Mỗi phần tử con trong một mảng hoặc trình vòng lặp phải có một chỗ dựa "khóa" duy nhất. Kiểm tra phương thức kết xuất của ListView.

Mục đích của cảnh báo này là gì? Sau thông báo, họ liên kết đến trang này , nơi thảo luận về những thứ hoàn chỉnh khác nhau, không liên quan gì đến phản ứng gốc, nhưng với phản ứng dựa trên web.

ListView của tôi được xây dựng với các câu lệnh đó:

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}
            />

    );
}

DataSource của tôi bao gồm một số thứ như:

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

Và ListView-Rows được hiển thị với những thứ như:

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

Mọi thứ đều hoạt động tốt và như mong đợi, ngoại trừ cảnh báo dường như hoàn toàn vô nghĩa đối với tôi.

Thêm thuộc tính khóa vào "DetailItem"-Class của tôi không giải quyết được vấn đề.

Đây là, những gì thực sự sẽ được chuyển đến ListView do kết quả của "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: 'xxxxxxxxx@hotmail.com',
I/ReactNativeJS( 1293):           headline: 'Email',
I/ReactNativeJS( 1293):           icon: 'fontawesome|envelope' },
I/ReactNativeJS( 1293):         { key: 6, type: 'moreInfo', text: '', headline: '', icon: '' } ] },

Như một khóa đã thấy, mỗi bản ghi có một thuộc tính khóa. Cảnh báo vẫn tồn tại.


1
Nhiều khả năng bạn DetailItemcần phải có chìa khóa. Nếu họ đã có các khóa duy nhất, bạn cần hiển thị các phương thức kết xuất khác ( renderHeader, renderDetailItem, renderSeparator). Chúng đang hoạt động tốt và được mong đợi cho đến khi nguồn dữ liệu được sửa đổi theo cách nào đó (ví dụ: các hàng bị xóa), lúc đó React sẽ không biết phải làm gì với chúng khi chúng không có một mã định danh duy nhất.
Pete TNT

Bạn có nghĩa là gì với "chìa khóa"? Thuộc tính được gọi là "chìa khóa"?
xóa


Nó không giải quyết được nó. Tôi đã thêm một thuộc tính chính vào cấu trúc dữ liệu của mình và cập nhật câu hỏi ban đầu với nhiều dữ liệu được tách hơn. Liệt kê dữ liệu thuần túy, dẫn đến DataSource, có một khóa cho mỗi bản ghi. Cảnh báo này vẫn còn.
xóa

Nó cũng có thể đến từ các phương thức kết xuất khác (renderHeader, renderDetailItem, renderSeparator)
Pete TNT

Câu trả lời:


93

Tôi đã gặp chính xác vấn đề giống như bạn trong một thời gian và sau khi xem xét một số gợi ý ở trên, cuối cùng tôi đã giải quyết được vấn đề.

Hóa ra (ít nhất là đối với tôi), tôi cần cung cấp một khóa (một chỗ dựa được gọi là 'khóa') cho thành phần tôi đang trả về từ phương thức renderSeparator của mình. Việc thêm khóa vào renderRow hoặc renderSectionHeader của tôi không làm được gì, nhưng việc thêm nó vào renderSeparator đã khiến cảnh báo biến mất.

Hy vọng rằng sẽ giúp.


Trong trường hợp của tôi, tôi vừa xóa renderSeparator và chuyển <Separator> của mình vào phần thân của giá trị trả về renderRow.
boatcoder

Điều tương tự ở đây đối với SectionList, phải thêm một cách rõ ràng thuộc tính với khóa tên cho mỗi thuộc tính itemđể làm cho RN hài lòng.
LeOn - Han Li

1
Trước khi đọc bài này, tôi đã lãng phí khoảng 8 giờ để theo dõi những gì tôi nghĩ là vấn đề với dữ liệu JSON của mình. Nếu có tràn ngăn xếp: taco: Tôi sẽ cho bạn một cái!
hoekma

76

Bạn cần cung cấp một chìa khóa .

Hãy thử làm điều này trong Hàng ListView của bạn nếu bạn có thuộc tính khóa:

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

Nếu không, hãy thử chỉ thêm mục làm khóa:

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

1
tôi thích câu trả lời này hơn, vì nó có mã để tôi có thể sao chép XD
Jovylle Bermudez

34

Bạn cũng có thể sử dụng số lần lặp (i) như keysau:

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

2
Điều này có thể không hoạt động khi mảng thay đổi. Câu trả lời này giải thích nó bằng một ví dụ: stackoverflow.com/a/43892905/960857
Chris

21

Thay đổi mã của bạn từ:

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

Đến:

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

Sau đó được giải quyết.


13

Thêm một 'khóa' hỗ trợ vào thành phần gốc hiển thị của danh sách.

<ScrollView>
      <List>
          {this.state.nationalities.map((prop, key) => {
             return (
               <ListItem key={key}>
                  <Text>{prop.name}</Text>
               </ListItem>
             );
          })}
      </List>
</ScrollView>

6

Cảnh báo này xuất hiện khi bạn không thêm khóa vào các mục trong danh sách của mình. As per react js Docs -

Các phím giúp React xác định những mục nào đã thay đổi, được thêm vào hoặc bị xóa. Các khóa phải được cấp cho các phần tử bên trong mảng để cung cấp cho các phần tử một danh tính ổn định:

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

Cách tốt nhất để chọn một khóa là sử dụng một chuỗi xác định duy nhất một mục danh sách giữa các anh chị em của nó. Thông thường, bạn sẽ sử dụng ID từ dữ liệu của mình làm khóa:

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

Khi bạn không có ID ổn định cho các mục được hiển thị, bạn có thể sử dụng chỉ mục mục làm khóa như một phương sách cuối cùng

const todoItems = todos.map((todo, index) =>
  // Only do this if items have no stable IDs
  <li key={index}>
    {todo.text}
  </li>
);

4

Tôi đã sửa nó bằng cách thêm một thuộc tính vào Thành phần renderSeparator, mã ở đây:

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

Từ khóa của cảnh báo này là "duy nhất", sectionID + rowID trả về một giá trị duy nhất trong ListView.


4

Kiểm tra: key = undef !!!

Bạn cũng nhận được thông báo cảnh báo:

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

nếu mã của bạn hoàn chỉnh đúng, nhưng nếu trên

<MyComponent key={someValue} />

someValue là không xác định !!! Vui lòng kiểm tra điều này đầu tiên. Bạn có thể tiết kiệm hàng giờ.


3

Giả sử phương thức renderDetailItem có chữ ký sau ...

(rowData, sectionID, rowID, highlightRow) 

Hãy thử làm điều này ...

<TouchableHighlight key={rowID} underlayColor='#dddddd'>

3

Mã cụ thể mà tôi đã sử dụng để sửa lỗi này là:

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

Tôi bao gồm mã cụ thể vì bạn cần các khóa là duy nhất - ngay cả đối với dấu phân cách. Nếu bạn làm điều gì đó tương tự, ví dụ, nếu bạn đặt điều này thành hằng số, bạn sẽ chỉ gặp một lỗi khó chịu khác về việc sử dụng lại các phím. Nếu bạn không biết JSX, việc xây dựng lệnh gọi lại tới JS để thực thi các phần khác nhau có thể khá khó khăn.

Và trên ListView, rõ ràng là đính kèm điều này:

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

Tín dụng cho coldbuffet và Nader Dabit, người đã chỉ cho tôi con đường này.


2

Đây là dựa trên sự hiểu biết của tôi. Hy vọng rằng nó hữu ích. Nó phải hiển thị một danh sách của bất kỳ thành phần nào như ví dụ phía sau. Thẻ gốc của mỗi thành phần cần phải có một key. Nó không cần phải là duy nhất. Nó không thể được key=0, key='0'vv Có vẻ như chìa khóa là vô dụng.

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

0

Có vẻ như cả hai điều kiện đều được đáp ứng, có lẽ chính ('liên hệ') là vấn đề

 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') );
}

không, liên hệ không phải là chìa khóa. Ở giữa, tôi đã thêm một thuộc tính thực có tên là "khóa" vào cấu trúc dữ liệu của mình và cập nhật câu hỏi của tôi để chuyển dữ liệu chi tiết hơn. Không có gì giúp ích. Cảnh báo vẫn còn.
xóa


0

Điều khiến tôi gặp phải vấn đề này là tôi nghĩ rằng nhu cầu về một khóa được áp dụng cho những gì trông giống như các phần tử HTML 'thực' hoặc DOM trái ngược với các phần tử JSX mà tôi đã xác định.

Tất nhiên với React, chúng tôi đang làm việc với DOM ảo nên các phần tử React JSX mà chúng tôi xác định <MyElement>cũng quan trọng đối với nó như các phần tử trông giống như các phần tử HTML HTML thực <div>.

Điều đó có ý nghĩa?


0

Trong trường hợp của tôi, tôi đang sử dụng chế độ xem "Thẻ" Semantic UI React. Sau khi tôi thêm khóa vào mỗi thẻ mà tôi đã tạo, cảnh báo sẽ biến mất, ví dụ:

return (
        <Card fluid key={'message-results-card'}>
          ...
        </Card>
)

-1

Nếu bạn đang sử dụng <Fade in>phần tử cho ứng dụng phản ứng, vui lòng thêm key={}thuộc tính vào phần tử đó, nếu không bạn sẽ thấy lỗi trong bảng điều khiển.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.