0

I have a list of Contacts in the Flat list, I have made a random color generator function that will generate random background colors for the contacts profile image placeholder with the user's First Name Initials. I have added a delete button to the list so that users can delete the specific contact. I have made a custom popup dialog to ask for the confirmation if user presses the delete button. I am fetching the list from the local realm database.

Now the issue is when I click on the delete button it re-renders the whole list, the background color of the user image placeholder changes every time I click on the delete button. But the expected behavior should be when I click on the delete button my custom popup will appear and if the user confirms to delete then the list can re-render but it is re-rendering by just clicking on the delete button. I am not familiar with react memo. I tried applying it on generateRandomColor function and even on custom popup but nothing worked. below is my raw code. Someone, please help, I am stuck for the last 2 days.

Contact list Code Below:-

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;

Custom Dialog Component Code Below:-

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;
Mantu
  • 1,017
  • 1
  • 10
  • 21

1 Answers1

0

Seperate ur contact card and custom popup into a component and. Pass the deleted contact to parent via props this will ensure there is no rerender as the state update on popup will be specific to that contact card and not to whole screen parent component

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;

contact component

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 this is just a brief overview you may need to make some changes to avoid errors **

Srinath gunnu
  • 162
  • 1
  • 12
  • This is not working brother, We can't add a custom dialog component inside the contact card component. Both are different and we can not wrap custom dialog components inside the container view of the contact card of the flat list. – Mantu Oct 12 '21 at 08:54
  • why can't u add custom dialog inside the contact card? – Srinath gunnu Oct 12 '21 at 10:16
  • Contact Card has a fixed container height and it will not be visible if i use i inside the contact card container. – Mantu Oct 12 '21 at 10:32
  • even the container height is fixed u can have a popup of full screen – Srinath gunnu Oct 12 '21 at 10:56