【问题标题】:How I can resolve this : Warning: Encountered two children with the same key, `%s`我如何解决这个问题:警告:遇到两个孩子使用相同的密钥,`%s`
【发布时间】:2020-02-28 22:53:37
【问题描述】:

我是 react-native 的新手,这不是我编写这个应用程序的人。

有人可以帮我解决这个错误吗,我认为是平面列表导致了这个错误,因为它只有在我加载页面或搜索列表中的内容时才会发生。我知道有很多关于这个错误的问题,但我没有找到适合我的解决方案。

Warning: Encountered two children with the same key,%s. Keys should be unique so that components maintain their identity across updates.

ContactScreen.js

import React from 'react';
import { Button, View, FlatList, Alert, StyleSheet, KeyboardAvoidingView } from 'react-native';
import { ListItem, SearchBar } from 'react-native-elements';
import Ionicons from 'react-native-vector-icons/Ionicons';
import { Contacts } from 'expo';
import * as Api from '../rest/api';
import theme from '../styles/theme.style';
import { Contact, ContactType } from '../models/Contact';

class ContactsScreen extends React.Component {
  static navigationOptions = ({ navigation }) => {
    return {
      headerTitle: "Contacts",
      headerRight: (
        <Button
          onPress={() => navigation.popToTop()}
          title="Déconnexion"
        />
      ),
    }
  };

  constructor(props) {
    super(props);

    this.state = {
      contacts: [],
      search: '',
      isFetching: false,
      display_contacts: []
    }
  }

  async componentDidMount() {
    this.getContactsAsync();
  }

  async getContactsAsync() {
    const permission = await Expo.Permissions.askAsync(Expo.Permissions.CONTACTS);
    if (permission.status !== 'granted') { return; }

    const contacts = await Contacts.getContactsAsync({
        fields: [
          Contacts.PHONE_NUMBERS,
          Contacts.EMAILS,
          Contacts.IMAGE
        ],
        pageSize: 100,
        pageOffset: 0,
    });


    const listContacts = [];
    if (contacts.total > 0) {
      for(var i in contacts.data) {
        let contact = contacts.data[i];
        let id = contact.id;
        let first_name = contact.firstName;
        let middle_name = contact.middleName;
        let last_name = contact.lastName;
        let email = "";
        if ("emails" in  contact && contact.emails.length > 0) {
          email = contact.emails[0].email;
        }
        let phone = "";
        if ("phoneNumbers" in contact && contact.phoneNumbers.length > 0) {
          phone = contact.phoneNumbers[0].number;
        }
        listContacts.push(new Contact(id, first_name, middle_name, last_name, email, phone, ContactType.UP));
      }
    }

    const soemanContacts = await Api.getContacts();
    if (soemanContacts.length > 0) {
      for(var i in soemanContacts) {
        let contact = soemanContacts[i];
        let id = contact.contact_id.toString();
        let first_name = contact.contact_first_name
        let last_name = contact.contact_last_name;
        let email = contact.contact_email;
        let phone = contact.contact_phone.toString();
        listContacts.push(new Contact(id, first_name, "", last_name, email, phone, ContactType.DOWN));
      }
    }

    listContacts.sort((a, b) => a.name.localeCompare(b.name));
    this.setState({contacts: listContacts});
    this.setState({ isFetching: false });
    this.updateSearch(null);
  }

  async addContactAsync(c) {
    const contact = {
      [Contacts.Fields.FirstName]: c.firstName,
      [Contacts.Fields.LastName]: c.lastName,
      [Contacts.Fields.phoneNumbers]: [
        {
          'number': c.phone
        },
      ], 
      [Contacts.Fields.Emails]: [
        {
          'email': c.email
        }
      ]
    }
    const contactId = await Contacts.addContactAsync(contact);
  }

  onRefresh() {
    this.setState({ isFetching: true }, function() { this.getContactsAsync() });
  }

  updateSearch = search => {
    this.setState({ search });
    if(!search) {
      this.setState({display_contacts: this.state.contacts});
    }
    else {

      const res = this.state.contacts.filter(contact => contact.name.toLowerCase().includes(search.toLowerCase()));
      console.log(res);
      this.setState({display_contacts: res});
      console.log("contact display "+ this.state.display_contacts);
    }
  };

  toggleContact(contact) {
    switch(contact.type) {
      case ContactType.SYNC:
        break;
      case ContactType.DOWN:
        this.addContactAsync(contact);
        break;
      case ContactType.UP:
        Api.addContact(contact);
        break;
    }
    /*Alert.alert(
      'Synchronisé',
      contact.name + 'est déjà synchronisé'
    );*/
  }

  renderSeparator = () => (
    <View style={{ height: 0.5, backgroundColor: 'grey', marginLeft: 0 }} />
  )

  render() {
    return (
      <View style={{ flex: 1 }}>
      <KeyboardAvoidingView style={{ justifyContent: 'flex-end' }} behavior="padding" enabled>
        <SearchBar
          platform="default"
          lightTheme={true}
          containerStyle={styles.searchBar}
          inputStyle={styles.textInput}
          placeholder="Type Here..."
          onChangeText={this.updateSearch}
          value={this.state.search}
          clearIcon
        />
        <FlatList
          data={this.state.display_contacts}
          onRefresh={() => this.onRefresh()}
          refreshing={this.state.isFetching}
          renderItem={this.renderItem}
          keyExtractor={contact => contact.id}
          ItemSeparatorComponent={this.renderSeparator}
          ListEmptyComponent={this.renderEmptyContainer()}
        />
      </KeyboardAvoidingView>
      </View>
    );
  }

  renderItem = (item) => {
    const contact = item.item;
    let icon_name = '';
    let icon_color = 'black';
    switch(contact.type) {
      case ContactType.SYNC:
        icon_name = 'ios-done-all';
        icon_color = 'green';
        break;
      case ContactType.DOWN:
        icon_name = 'ios-arrow-down';
        break;
      case ContactType.UP:
        icon_name = 'ios-arrow-up';
        break;
    }
    return (
      <ListItem
        onPress={ () => this.toggleContact(contact) }
        roundAvatar
        title={contact.name}
        subtitle={contact.phone}
        //avatar={{ uri: item.avatar }}
        containerStyle={{ borderBottomWidth: 0 }}
        rightIcon={<Ionicons name={icon_name} size={20} color={icon_color}/>}
      />
    );
  }

  renderEmptyContainer() {
    return (
      <View>
      </View>
    )
  }
}

const styles = StyleSheet.create({
  searchBar: {
    backgroundColor: theme.PRIMARY_COLOR
  },
  textInput: {
    backgroundColor: theme.PRIMARY_COLOR,
    color: 'white'
  }
});

export default ContactsScreen;

我在这个应用程序中使用 react-native 和 expo。

【问题讨论】:

    标签: react-native expo


    【解决方案1】:

    只需在你的平面列表中执行此操作

    keyExtractor={(item, index) => String(index)}
    

    【讨论】:

    • 您好,我遇到了同样的错误,但是这个解决方案对我不起作用。任何解决方案
    • 只需添加这一行(快速刷新)什么都没有发生,但重新加载应用程序后这对我有用。
    【解决方案2】:

    我认为您的一些contact.id 是相同的。所以你可以得到这个警告。如果在 FlatList 中设置列​​表的索引号,则无法显示此内容。

    keyExtractor={(contact, index) => String(index)}
    

    【讨论】:

      【解决方案3】:

      不要即时使用索引构建键。如果你想构建密钥,你应该尽可能在渲染之前完成它。

      如果您的联系人有保证唯一的 ID,您应该使用它。如果他们不这样做,您应该在数据出现在视图中之前使用生成唯一键的函数构建一个键

      示例代码:

        // Math.random should be unique because of its seeding algorithm.
        // Convert it to base 36 (numbers + letters), and grab the first 9 characters
        // after the decimal.
        const keyGenerator = () => '_' + Math.random().toString(36).substr(2, 9)
        // in component
        key={contact.key}
      

      【讨论】:

      • 像你这样的人已经买进了天堂。请让圣彼得知道你在地狱里度过了你的时光。感谢各位大神对密钥生成的掌握。
      • 嗨,我有这个:const [categories, setCategories] = useState(['One Punch', 'Samurai X', 'Dragon Ball']) 并在地图中:return &lt;li key={ category }&gt; {category} &lt;/li&gt; 我是新手,如何将“keyGenerator”上方的代码应用于我的代码?
      【解决方案4】:

      只需在您的平面列表中执行此操作

       keyExtractor={(id) => { id.toString(); }}
      

      【讨论】:

        【解决方案5】:

        我遇到了同样的错误,我在这种情况下修复了:

        不要以这种方式编码(使用async) - 这将在每个项目中重复渲染多次(我不知道为什么)

        Stub_Func = async () => {
          const status = await Ask_Permission(...);
          if(status) {
            const result = await Get_Result(...);
            this.setState({data: result});
          }
        }
        componentDidMount() {
          this.Stub_Func();
        }
        

        尝试这样的事情(使用then):

        Stub_Func = () => {
          Ask_Permission(...).then(status=> {
            if(status) {
              Get_Result(...).then(result=> {
                this.setState({data:result});
              }).catch(err => {
                throw(err);
              });
            }
          }).catch(err => {
            throw(err)
          });
        }
        componentDidMount() {
          this.Stub_Func();
        }
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2019-07-17
          • 2018-10-06
          • 2017-06-01
          • 1970-01-01
          • 2021-02-22
          • 2022-01-10
          • 1970-01-01
          • 2017-12-15
          相关资源
          最近更新 更多