【问题标题】:Pressing on Delete button in React native flatlist re-renders whole list在 React 本机平面列表中按下 Delete 按钮会重新渲染整个列表
【发布时间】:2021-10-12 05:27:44
【问题描述】:

我在平面列表中有一个联系人列表,我制作了一个随机颜色生成器函数,它将为带有用户名字缩写的联系人个人资料图像占位符生成随机背景颜色。我在列表中添加了一个删除按钮,以便用户可以删除特定的联系人。如果用户按下删除按钮,我已经制作了一个自定义弹出对话框来要求确认。我正在从本地领域数据库中获取列表。

现在的问题是,当我单击删除按钮时,它会重新渲染整个列表,每次单击删除按钮时,用户图像占位符的背景颜色都会发生变化。但是预期的行为应该是当我单击删除按钮时,我的自定义弹出窗口将出现,如果用户确认删除,则列表可以重新呈现,但只需单击删除按钮即可重新呈现。我不熟悉反应备忘录。我尝试在 generateRandomColor 函数甚至自定义弹出窗口上应用它,但没有任何效果。下面是我的原始代码。有人,请帮助,我在过去 2 天被卡住了。

联系人列表代码如下:-

import React from 'react';
import {useState, useEffect, useCallback} from 'react';
import {
  View,
  Text,
  FlatList,
  StatusBar,
  SafeAreaView,
  TouchableOpacity,
  StyleSheet,
  Linking,
  Animated,
  Modal,
} from 'react-native';
import {CustomDialog} from '@components/CustomDialog';

import {Header} from '@components/Header';
import Fonts from '@utils/Fonts';
import {Colors} from '@theme';
import WhatsappIcon from '@images/svgs/whatsapp.svg';
import DeleteIcon from '@images/svgs/delete.svg';
import {
  realm,
  insertNewContactList,
  updateContactList,
  deleteContactList,
  queryAllContactLists,
} from '@database/realmDB';

const RecentSaved = ({navigation}) => {
  const [recentSavedContacts, setRecentSavedContacts] = useState([]);
  const [visible, setVisible] = useState(false);

  useEffect(() => {
    const tasks = realm.objects('Contact');
    // set state to the initial value of your realm objects
    setRecentSavedContacts([...tasks]);
    tasks.addListener(() => {
      // update state of tasks to the updated value
      setRecentSavedContacts([...tasks]);
    });

    return () => {
      // Remember to remove the listener when you're done!
      tasks.removeAllListeners();

      // Call the close() method when done with a realm instance to avoid memory leaks.
      realm.close();
    };
  }, []);

  const InitialIcon = ({initials}) => {
    return (
      <View
        style={[
          Styles.initialsIconContainer,
          {backgroundColor: generateColor()},
        ]}>
        <Text style={Styles.initialsIconText}>{initials}</Text>
      </View>
    );
  };

  const generateColor = () => {
    const randomColor = Math.floor(Math.random() * 16777215)
      .toString(16)
      .padStart(6, '0');
    return `#${randomColor}`;
  };


  const onDelete = id => {
    // deleteContactList(id)
    //   .then()
    //   .catch(error => {
    //     alert(`Failed to delete todoList with id = ${id}, error=${error}`);
    //   });
    setVisible(true);
  };

  const recentContactCards = ({item, index}) => {
    return (
      <View style={Styles.flatListCard}>
        <View style={Styles.flatListSubView}>
          <View style={{flexDirection: 'row', marginLeft: 10}}>
            <InitialIcon
              initials={
                item.name
                  ? item.name.slice(0, 2).toUpperCase()
                  : `+${item.number.slice(0, 2)}`
              }
            />
            <View style={{justifyContent: 'center'}}>
              {item.name ? (
                <Text style={Styles.nameTitle}>{item.name}</Text>
              ) : null}

              <Text style={item.name ? Styles.numberTitle : Styles.nameTitle}>
                +{item.number}
              </Text>
            </View>
          </View>
          <View style={{flexDirection: 'row'}}>
            <TouchableOpacity
              style={{marginRight: 15}}>
              <WhatsappIcon width={25} height={25} />
            </TouchableOpacity>
            <TouchableOpacity
              onPress={() => onDelete(item.id)}
              style={{marginRight: 10}}>
              <DeleteIcon width={25} height={25} />
            </TouchableOpacity>
          </View>
        </View>
        <View style={Styles.separatorLine} />
      </View>
    );
  };

  return (
    <SafeAreaView style={Styles.safeAreaContainer}>
      <StatusBar barStyle="light-content" backgroundColor={Colors.AppTheme} />
      <Header title="Recent" />
      <FlatList
        data={recentSavedContacts}
        contentContainerStyle={Styles.flatListContainer}
        keyExtractor={(item, index) => Math.random().toString()}
        renderItem={recentContactCards}
        ListEmptyComponent={() => {
          return (
            <View style={Styles.emptyListView}>
              <Text>There are no saved Contacts</Text>
            </View>
          );
        }}
      />

      <CustomDialog visible={visible}>
        <View style={{alignItems: 'center'}}></View>
        <View style={{alignItems: 'center'}}>
          <DeleteIcon width={45} height={45} />
        </View>

        <Text style={Styles.nameTitle}>
          Are you sure you want to delete this contact?
        </Text>
        <View
          style={{
            flexDirection: 'row',
            justifyContent: 'space-between',
            width: '90%',
            alignSelf: 'center',
          }}>
          <TouchableOpacity
            style={{
              borderWidth: 1,
              borderRadius: 10,
              padding: 10,
              backgroundColor: 'red',
              width: '40%',
            }}
            onPress={() => setVisible(false)}>
            <Text style={[Styles.nameTitle, {marginLeft: 15, color: 'white'}]}>
              Cancel
            </Text>
          </TouchableOpacity>
          <TouchableOpacity
            style={{
              borderWidth: 1,
              borderRadius: 10,
              padding: 10,
              backgroundColor: 'red',
              width: '40%',
            }}
            onPress={() => setVisible(false)}>
            <Text style={[Styles.nameTitle, {marginLeft: 15, color: 'white'}]}>
              Confirm
            </Text>
          </TouchableOpacity>
        </View>
      </CustomDialog>
    </SafeAreaView>
  );
};

const Styles = StyleSheet.create({
  safeAreaContainer: {flex: 1, backgroundColor: 'white'},
  initialsIconContainer: {
    alignItems: 'center',
    justifyContent: 'center',
    borderRadius: 25,
    width: 50,
    height: 50,
  },
  initialsIconText: {color: 'white', fontSize: 20},
  flatListContainer: {
    backgroundColor: Colors.white,
    flexGrow: 1,
  },
  flatListCard: {
    height: 70,
    backgroundColor: Colors.white,
    margin: 10,
    alignItems: 'center',
    borderRadius: 35,
  },
  flatListSubView: {
    flexDirection: 'row',
    justifyContent: 'space-between',
    alignItems: 'center',
    width: '100%',
  },
  emptyListView: {
    flexGrow: 1,
    justifyContent: 'center',
    alignItems: 'center',
  },
  nameTitle: {
    ...Fonts.openSansSemiBold,
    fontSize: 15,
    color: 'black',
    marginLeft: 20,
  },
  numberTitle: {
    ...Fonts.openSansRegular,
    fontSize: 15,
    color: 'gray',
    marginLeft: 20,
  },
  separatorLine: {
    height: 2,
    backgroundColor: '#F2F2F2',
    width: '100%',
    position: 'absolute',
    bottom: 2,
  },
  modalBackGround: {
    flex: 1,
    backgroundColor: 'rgba(0,0,0,0.5)',
    justifyContent: 'center',
    alignItems: 'center',
  },
  modalContainer: {
    width: '80%',
    backgroundColor: 'white',
    paddingHorizontal: 20,
    paddingVertical: 30,
    borderRadius: 20,
    elevation: 20,
  },
  header: {
    width: '100%',
    height: 40,
    alignItems: 'flex-end',
    justifyContent: 'center',
  },
});

export default RecentSaved;

自定义对话框组件代码如下:-

import React, {useState, useEffect, useCallback} from 'react';
import {View, StyleSheet, Animated, Modal} from 'react-native';

const CustomDialog = ({visible, children}) => {
  const [showModal, setShowModal] = useState(visible);
  const scaleValue = React.useRef(new Animated.Value(0)).current;
  useEffect(() => {
    toggleModal();
  }, [visible]);
  const toggleModal = () => {
    if (visible) {
      setShowModal(true);
      Animated.spring(scaleValue, {
        toValue: 1,
        duration: 300,
        useNativeDriver: true,
      }).start();
    } else {
      setTimeout(() => setShowModal(false), 200);
      Animated.timing(scaleValue, {
        toValue: 0,
        duration: 300,
        useNativeDriver: true,
      }).start();
    }
  };
  return (
    <Modal transparent visible={showModal}>
      <View style={Styles.modalBackGround}>
        <Animated.View
          style={[Styles.modalContainer, {transform: [{scale: scaleValue}]}]}>
          {children}
        </Animated.View>
      </View>
    </Modal>
  );
};

const Styles = StyleSheet.create({
  modalBackGround: {
    flex: 1,
    backgroundColor: 'rgba(0,0,0,0.5)',
    justifyContent: 'center',
    alignItems: 'center',
  },
  modalContainer: {
    width: '80%',
    backgroundColor: 'white',
    paddingHorizontal: 20,
    paddingVertical: 30,
    borderRadius: 20,
    elevation: 20,
  },
  header: {
    width: '100%',
    height: 40,
    alignItems: 'flex-end',
    justifyContent: 'center',
  },
});

export default CustomDialog;

【问题讨论】:

    标签: react-native optimization react-native-flatlist react-memo


    【解决方案1】:

    将您的联系人卡片和自定义弹出窗口分离到一个组件中。通过道具将已删除的联系人传递给父级,这将确保不会重新渲染,因为弹出窗口上的状态更新将特定于该联系人卡片而不是整个屏幕 父组件

    import {useState, useEffect, useCallback} from 'react';
    import {
      View,
      Text,
      FlatList,
      StatusBar,
      SafeAreaView,
      TouchableOpacity,
      StyleSheet,
      Linking,
      Animated,
      Modal,
    } from 'react-native';
    
    import {Header} from '@components/Header';
    import Fonts from '@utils/Fonts';
    import {Colors} from '@theme';
    import WhatsappIcon from '@images/svgs/whatsapp.svg';
    import DeleteIcon from '@images/svgs/delete.svg';
    import {
      realm,
      insertNewContactList,
      updateContactList,
      deleteContactList,
      queryAllContactLists,
    } from '@database/realmDB';
    
    
    const RecentSaved = ({navigation}) => {
      const [recentSavedContacts, setRecentSavedContacts] = useState([]);
      
      useEffect(() => {
        const tasks = realm.objects('Contact');
        // set state to the initial value of your realm objects
        setRecentSavedContacts([...tasks]);
        tasks.addListener(() => {
          // update state of tasks to the updated value
          setRecentSavedContacts([...tasks]);
        });
    
        return () => {
          // Remember to remove the listener when you're done!
          tasks.removeAllListeners();
    
          // Call the close() method when done with a realm instance to avoid memory leaks.
          realm.close();
        };
      }, []);
    
     const onDelete = id => {
         deleteContactList(id)
           .then()
           .catch(error => {
             alert(`Failed to delete todoList with id = ${id}, error=${error}`);
           });
     
        //update ur contactlist
    
      };
    
    
      return (
        <SafeAreaView style={Styles.safeAreaContainer}>
          <StatusBar barStyle="light-content" backgroundColor={Colors.AppTheme} />
          <Header title="Recent" />
          <FlatList
            data={recentSavedContacts}
            contentContainerStyle={Styles.flatListContainer}
            keyExtractor={(item, index) => Math.random().toString()}
            renderItem={({item,index})=>{
            return
            <recentContactCards delete=onDelet(item.id)/>
            }}
            ListEmptyComponent={() => {
              return (
                <View style={Styles.emptyListView}>
                  <Text>There are no saved Contacts</Text>
                </View>
              );
            }}
          />
    
          
        </SafeAreaView>
      );
    };
    
    const Styles = StyleSheet.create({
      safeAreaContainer: {flex: 1, backgroundColor: 'white'},
      initialsIconContainer: {
        alignItems: 'center',
        justifyContent: 'center',
        borderRadius: 25,
        width: 50,
        height: 50,
      },
      initialsIconText: {color: 'white', fontSize: 20},
      flatListContainer: {
        backgroundColor: Colors.white,
        flexGrow: 1,
      },
      flatListCard: {
        height: 70,
        backgroundColor: Colors.white,
        margin: 10,
        alignItems: 'center',
        borderRadius: 35,
      },
      flatListSubView: {
        flexDirection: 'row',
        justifyContent: 'space-between',
        alignItems: 'center',
        width: '100%',
      },
      emptyListView: {
        flexGrow: 1,
        justifyContent: 'center',
        alignItems: 'center',
      },
      nameTitle: {
        ...Fonts.openSansSemiBold,
        fontSize: 15,
        color: 'black',
        marginLeft: 20,
      },
      numberTitle: {
        ...Fonts.openSansRegular,
        fontSize: 15,
        color: 'gray',
        marginLeft: 20,
      },
      separatorLine: {
        height: 2,
        backgroundColor: '#F2F2F2',
        width: '100%',
        position: 'absolute',
        bottom: 2,
      },
      modalBackGround: {
        flex: 1,
        backgroundColor: 'rgba(0,0,0,0.5)',
        justifyContent: 'center',
        alignItems: 'center',
      },
      modalContainer: {
        width: '80%',
        backgroundColor: 'white',
        paddingHorizontal: 20,
        paddingVertical: 30,
        borderRadius: 20,
        elevation: 20,
      },
      header: {
        width: '100%',
        height: 40,
        alignItems: 'flex-end',
        justifyContent: 'center',
      },
    });
    
    export default RecentSaved;
    

    联系人组件

    import React from 'react';
    import {useState, useEffect, useCallback} from 'react';
    import {
      View,
      Text,
      FlatList,
      StatusBar,
      SafeAreaView,
      TouchableOpacity,
      StyleSheet,
      Linking,
      Animated,
      Modal,
    } from 'react-native';
    
    import {Header} from '@components/Header';
    import Fonts from '@utils/Fonts';
    import {Colors} from '@theme';
    import WhatsappIcon from '@images/svgs/whatsapp.svg';
    import DeleteIcon from '@images/svgs/delete.svg';
    import {
      realm,
      insertNewContactList,
      updateContactList,
      deleteContactList,
      queryAllContactLists,
    } from '@database/realmDB';
    
    
    
     
      const recentContactCards = ({item, index}) => {
      const [visible, setVisible] = useState(false);
        const InitialIcon = ({initials}) => {
        return (
          <View
            style={[
              Styles.initialsIconContainer,
              {backgroundColor: generateColor()},
            ]}>
            <Text style={Styles.initialsIconText}>{initials}</Text>
          </View>
        );
      };
    
      const generateColor = () => {
        const randomColor = Math.floor(Math.random() * 16777215)
          .toString(16)
          .padStart(6, '0');
        return `#${randomColor}`;
      };
    
    
    
    const onDelete=()=>{
      setvisible(true)
    }
    const RequestDeleted()=>{
        props.delete?.()
        setvisible(false)
    
    }
      
    
        return (
          <View style={Styles.flatListCard}>
            <View style={Styles.flatListSubView}>
              <View style={{flexDirection: 'row', marginLeft: 10}}>
                <InitialIcon
                  initials={
                    item.name
                      ? item.name.slice(0, 2).toUpperCase()
                      : `+${item.number.slice(0, 2)}`
                  }
                />
                <View style={{justifyContent: 'center'}}>
                  {item.name ? (
                    <Text style={Styles.nameTitle}>{item.name}</Text>
                  ) : null}
    
                  <Text style={item.name ? Styles.numberTitle : Styles.nameTitle}>
                    +{item.number}
                  </Text>
                </View>
              </View>
              <View style={{flexDirection: 'row'}}>
                <TouchableOpacity
                  style={{marginRight: 15}}>
                  <WhatsappIcon width={25} height={25} />
                </TouchableOpacity>
                <TouchableOpacity
                  onPress={() => onDelete(item.id)}
                  style={{marginRight: 10}}>
                  <DeleteIcon width={25} height={25} />
                </TouchableOpacity>
              </View>
            </View>
            <View style={Styles.separatorLine} />
    
            <CustomDialog visible={visible}>
            <View style={{alignItems: 'center'}}></View>
            <View style={{alignItems: 'center'}}>
              <DeleteIcon width={45} height={45} />
            </View>
    
            <Text style={Styles.nameTitle}>
              Are you sure you want to delete this contact?
            </Text>
            <View
              style={{
                flexDirection: 'row',
                justifyContent: 'space-between',
                width: '90%',
                alignSelf: 'center',
              }}>
              <TouchableOpacity
                style={{
                  borderWidth: 1,
                  borderRadius: 10,
                  padding: 10,
                  backgroundColor: 'red',
                  width: '40%',
                }}
                onPress={() => setVisible(false)}>
                <Text style={[Styles.nameTitle, {marginLeft: 15, color: 'white'}]}>
                  Cancel
                </Text>
              </TouchableOpacity>
              <TouchableOpacity
                style={{
                  borderWidth: 1,
                  borderRadius: 10,
                  padding: 10,
                  backgroundColor: 'red',
                  width: '40%',
                }}
                onPress={() => {RequestDeleted()}}>
                <Text style={[Styles.nameTitle, {marginLeft: 15, color: 'white'}]}>
                  Confirm
                </Text>
              </TouchableOpacity>
            </View>
          </CustomDialog>
          </View>
        );
      };
    export default recentContactCards
    

    **PS 这只是一个简短的概述,您可能需要进行一些更改以避免错误 **

    【讨论】:

    • 这不行兄弟,我们不能在联系人卡片组件中添加自定义对话框组件。两者是不同的,我们不能将自定义对话框组件包装在平面列表的联系人卡片的容器视图中。
    • 为什么不能在联系人卡片中添加自定义对话框?
    • 联系人卡片有一个固定的容器高度,如果我在联系人卡片容器中使用 i ,它将不可见。
    • 即使容器高度是固定的,你也可以全屏弹出
    猜你喜欢
    • 1970-01-01
    • 2011-05-09
    • 1970-01-01
    • 1970-01-01
    • 2020-11-15
    • 2020-01-04
    • 2017-09-09
    • 1970-01-01
    • 2018-04-10
    相关资源
    最近更新 更多