React Native Flatlist with radio buttons works slow - react-native

I have flatlist with custom radio buttons implementaion, but when i tap on flatlist item it waits about second before changes to active button. I tried to useCallback with my renderItem and itemKeyExtractor functions but it doesnt help me.
Here is the code of my screen component:
export const PickOrganizationScreen = (props) => {
const { navigation, setOrganization, loadProcedureProviders, items, isLoading, procedureId, organizationId } = props;
useFocusEffect(
React.useCallback(()=>{loadProcedureProviders(procedureId);},[loadProcedureProviders, procedureId])
);
const renderItem = ({item}) => {
return (
<OrganizationItem
title={item.Title}
checked = {item.Id === organizationId}
onPress={() => {
setOrganization(item.Id, item.Title);
}}
/>);
};
const itemKeyExtractor = (item) => item.Id;
return (
isLoading ? (
<Spinner />
) : (
items.length > 0 ? (
<View style={styles.container}>
<View style={styles.paragraph}>
<Text style={styles.subtitle}>{I18n.t('pickOrganization')}</Text>
</View>
<View style={styles.alertMessage}>
<View style={styles.alertIcon}>
<Icon name="info" width={32} height={32} fill={Colors.primaryRed} />
</View>
<View>
<Text style={styles.alertText}>{I18n.t('pickOrganizationToRegisterService')}</Text>
</View>
</View>
<FlatList
data = {items}
renderItem = {renderItem}
keyExtractor = {itemKeyExtractor}
extraData={organizationId}
/>
<View style={styles.buttonWrapper}>
<ScreenButton
title={I18n.t('goToRegister')}
onPress={ () => {
navigation.navigate('ServiceSummary');
}}/>
</View>
</View>
) : (
<NotFound extraText={I18n.t('notFoundExtra')}/>
)
)
);
};
Here is the code of flatlist item component:
export const OrganizationItem = ({title, checked, onPress}) => {
return (
<TouchableOpacity onPress={onPress}>
<View style={styles.itemContainer}>
<View style={styles.itemIcon}>
{ checked ? (
<Icon name="radio-button-on" width={16} height={16} fill={Colors.primaryRed} />
) :
(
<Icon name="radio-button-off" width={16} height={16} fill={Colors.gray} />
)
}
</View>
<View style={styles.itemText}>
<Text style={styles.title}>{title}</Text>
</View>
</View>
</TouchableOpacity>
);
};

Try memoizing OrganizationItem. Does the onPress in OrganizationItem change the items object ? If so, it will re-render the whole Flatlist even when one small change is made. If you memoize, the components memoized won't rerender unless the props passed to it changes.
export const OrganizationItem = React.memo(({title, checked, onPress}) => {
return (
<TouchableOpacity onPress={onPress}>
<View style={styles.itemContainer}>
<View style={styles.itemIcon}>
{ checked ? (
<Icon name="radio-button-on" width={16} height={16} fill={Colors.primaryRed} />
) :
(
<Icon name="radio-button-off" width={16} height={16} fill={Colors.gray} />
)
}
</View>
<View style={styles.itemText}>
<Text style={styles.title}>{title}</Text>
</View>
</View>
</TouchableOpacity>
);
});

Related

Giving props to other component React Native not working

I'm trying to give the data of a certain person to the other component but for some reason this only gives errors.
I tried doing that with the props method as it should but that does not work.
What can be the solution?
Parent:
export default function EditFamily({ navigation, props, person, inheritors }) {
const [shouldShowAdd, setShouldShowAdd] = useState(null);
const [shouldShowAddChild, setShouldShowAddChild] = useState(null);
function renderFamily(family, key) {
return (
<View key={key} style={styles.family}>
<View style={styles.inheritors}>
{family.inheritors.map((inheritor) => {
return (
<Add
key={inheritor.id}
person={inheritor}
setShouldShowAdd={setShouldShowAdd}
/>
);
})}
</View>
{family.children && family.children.length ? (
<View style={styles.children}>
{family.children.map((child, index) => renderFamily(child, index))}
</View>
) : null}
</View>
);
}
return (
<View style={styles.container}>
<SafeAreaView style={styles.container}>
{shouldShowAdd ? (
<View style={styles.details}>
<View style={styles.pressView}>
<Pressable
onPress={() => setShouldShowAdd(null)}
style={styles.pressX}
>
<Text>X</Text>
</Pressable>
</View>
<Pressable>
<Text>Edit date of {shouldShowAdd.firstname}</Text>
</Pressable>
<Pressable
onPress={() => setShouldShowAddChild(!shouldShowAddChild)}
>
<Text>Add child</Text>
</Pressable>
{shouldShowAddChild && <AddChild />}
<Pressable>
<Text>Add Partner</Text>
</Pressable>
<Pressable>
<Text>Add Parent</Text>
</Pressable>
</View>
) : null}
<ScrollView>
<ScrollView horizontal={true}>{renderFamily(data)}</ScrollView>
</ScrollView>
</SafeAreaView>
<StatusBar style="auto" />
</View>
);
}
Component that gives data to the parent:
export default function Add({ person, setShouldShowAdd }) {
let mijnFoto = 'http://192.168.1.36/CodingApp/assets/' + person.image;
return (
<TouchableOpacity
style={styles.person}
onPress={() => setShouldShowAdd(person)}
>
<Text style={styles.text}>
{person.firstname} {person.lastname}
</Text>
</TouchableOpacity>
);
}
The component that should for example be able to show the Id of the person that's been clicked:
export default function AddChild({ setShouldShowAdd }) {
return (
<View>
<Text>hoi id:{person.id}</Text>
</View>
);
}

React Native Open Modal from another Modal

Hi I am new to React Native.
I want to open a modal from another modal.
I have a modal showing event details and user can delete this event. When they press delete button, I want to add another modal saying 'are you sure ?' as in the picture
[![enter image description here][1]][1]
My code doesn't work. I used onModalHide with setTimeOut to close the first one and opend the second one. But did not work. What can be the error here ?
import React, {useRef, useState} from 'react';
import {View, ScrollView, Text, Pressable} from 'react-native';
import styles from '#/screens/dashboard/events/CreatedEventDetails.styles';
import Modal from 'react-native-modal';
import common from '#/config/common';
import {useDispatch, useSelector} from 'react-redux';
import FastImage from 'react-native-fast-image';
import images from '#/config/images';
import {Icon, LoadingIndicator} from '#/components';
import {
closeEventDetailsModal,
fetchEventsList,
handleDeleteEvent,
handleWithDrawEvent,
LOADING,
} from '#/store/events/events';
import {EVENT_STATUS} from '#/constants/eventStatus';
import {strings} from '#/localization';
import moment from 'moment';
const CreatedEventDetails = ({isVisible, closeModal}) => {
const createdEventDetails = useSelector(state => state?.events?.eventDetails);
const userInfo = useSelector(state => state.profile.user);
const dispatch = useDispatch();
const currentEventId = useSelector(state => state.events.currentEventId);
const isLoading =
useSelector(state => state?.events?.eventResponseStatus) === LOADING;
const capitilizedLetter = createdEventDetails?.name
?.toLowerCase()
.charAt(0)
.toUpperCase();
const restOfTheName = createdEventDetails?.name?.slice(1);
const deleteEvent = id => {
dispatch(handleDeleteEvent(id));
dispatch(closeEventDetailsModal());
};
const withDrawEvent = id => {
dispatch(handleWithDrawEvent(id));
dispatch(closeEventDetailsModal());
};
function getBgColor(condition) {
switch (condition) {
case EVENT_STATUS.STATUS_NEW:
return '#ef9d50';
case EVENT_STATUS.STATUS_ACCEPTED:
return '#a1dc6a';
case EVENT_STATUS.STATUS_TENTATIVE:
return 'blue';
case EVENT_STATUS.STATUS_REJECTED:
return 'red';
default:
return '#ef9d50';
}
}
// to change the modal action according to created events or accepted events, we have to check
// if the logged in user id is matching with event owner id. ( delete - withdraw action )
const isOwner = createdEventDetails?.owner?.id === userInfo?.id;
const createdEventDate = createdEventDetails?.event_date;
const formattedDate = moment(createdEventDate).format('DD MMM, ddd');
const fromTime = createdEventDetails?.from_time?.toString();
const formattedFromTime = moment(fromTime, 'hh:mm').format('LT');
const toTime = createdEventDetails?.to_time?.toString();
const formattedToTime = moment(toTime, 'hh:mm').format('LT');
// second modal state
const [isDeleteModalVisible, setIsDeleteModalVisible] = useState(false);
// second modal function
const toggleModal = () => {
// when the second modal is open, I close the first one here.
dispatch(closeEventDetailsModal());
setIsDeleteModalVisible(!isDeleteModalVisible);
};
return (
<>
<View style={styles.modalContainer}>
<Modal
isVisible={isVisible}
propagateSwipe
onBackdropPress={() => this.setState(isVisible)}
onModalHide={() => {
setTimeout(() => {
setIsDeleteModalVisible(!isDeleteModalVisible);
}, 100);
}}
onModalWillHide={() => {
setTimeout(() => {
dispatch(fetchEventsList());
}, 500);
}}>
<View style={styles.content}>
{/* we have to check if event id is matching with currentEventId to prevent undefined problem
and also modal detail can have previous event detail. this will prevent the issue */}
{createdEventDetails.id !== currentEventId ? (
<LoadingIndicator visible />
) : (
<View>
<LoadingIndicator visible={isLoading} />
<View style={styles.closeBtnContainer}>
<Pressable style={styles.closeBtn} onPress={closeModal}>
<Text style={styles.closeText}>X</Text>
</Pressable>
</View>
<View>
<Text
style={
styles.contentTitle
}>{`${capitilizedLetter}${restOfTheName}`}</Text>
<Text style={styles.contenSubtTitle}>
{formattedDate}
{'\n'}
{formattedFromTime} - {formattedToTime}
</Text>
<Text style={styles.contentText}>
At {createdEventDetails?.location}
</Text>
<View style={styles.participantsContainer}>
<Text style={styles.contentTitle}>
{strings.eventDetails.participantsTitle} (
{createdEventDetails?.invites?.length})
</Text>
<View style={common.stickyButtonLine} />
<ScrollView>
{createdEventDetails?.invites?.map(invite => (
<View style={styles.checkboxContainer} key={invite.id}>
<View style={styles.imageContainer}>
{invite?.user?.thumb ? (
<FastImage
source={{uri: invite?.user?.thumb}}
style={styles.listItemUserImage}
resizeMode={FastImage.resizeMode.cover}
/>
) : (
<Icon
icon={images.ic_user}
style={styles.noUserIcon}
/>
)}
<Text style={styles.contentPersonName}>
{invite?.user?.name}
</Text>
</View>
<View
style={{
backgroundColor: getBgColor(invite?.status),
width: 25,
height: 25,
borderRadius: 25,
}}>
<Text>{''}</Text>
</View>
</View>
))}
</ScrollView>
</View>
<View>
<View>
<Text style={styles.contentTitle}>
{strings.eventDetails.hobbiesTitle} (
{createdEventDetails?.hobbies?.length})
</Text>
<View style={common.stickyButtonLine} />
<View style={styles.hobbiesContainer}>
{createdEventDetails?.hobbies?.map(hobby => (
<View style={styles.emojiContainer} key={hobby.id}>
<Text>{hobby.emoji}</Text>
</View>
))}
</View>
<View style={common.stickyButtonLine} />
</View>
<View style={[styles.buttons, styles.singleButtonAlign]}>
{isOwner ? (
<>
<Pressable
style={styles.decline}
onPress={toggleModal}
// onPress={() =>
// deleteEvent(createdEventDetails?.id)
// }
>
<Text style={styles.declineText}>
{strings.eventDetails.delete}
</Text>
</Pressable>
// second modal content here
{/* <Modal
isVisible={isDeleteModalVisible}
propagateSwipe>
<View style={styles.content}>
<Text>Hello!</Text>
<Pressable
title="Hide modal"
onPress={toggleModal}
/>
</View>
</Modal> */}
</>
) : (
<Pressable
style={styles.decline}
onPress={() =>
withDrawEvent(createdEventDetails?.id)
}>
<Text style={styles.declineText}>
{strings.eventDetails.withdraw}
</Text>
</Pressable>
)}
</View>
</View>
</View>
</View>
)}
</View>
</Modal>
</View>
// second modal content here
<View style={styles.modalContainer}>
<Modal isVisible={isDeleteModalVisible} propagateSwipe>
<View style={styles.content}>
<Text>Hello!</Text>
<Pressable title="Hide modal" onPress={toggleModal} />
</View>
</Modal>
</View>
</>
);
};;
export default CreatedEventDetails;
````
[1]: https://i.stack.imgur.com/BUMfa.png
Here are some way of debugging.
first check both the modal is showing up by passing harcoded true value for one by one.
if its working correctly, remove those timeouts and apicalls and
just check with useState only.
if all are fine then try to call
the api.

Correct way to use memo in Flatlist react native

Good evening everyone .
I have created the following Flatlist :
<View style={{ flex: 1 }}>
<FlatList
ListFooterComponent={
loadMore && page < pageMax ? (
<ActivityIndicator color={Colors.grey40} />
) : (
<View />
)
}
ListFooterComponentStyle={{ height: 200 }}
contentContainerStyle={styles.listExercise}
keyExtractor={() => uuid.v4()}
data={exercises}
renderItem={renderItemRE}
removeClippedSubviews={true}
onEndReached={() => {
setOnEndReachedCalledDuringMomentum(true);
}}
onMomentumScrollEnd={loadMoreFc}
onEndReachedThreshold={0.3}
ListEmptyComponent={
<Text contentW B18>
Nessuna Esercizio presente
</Text>
}
/>
</View>
This is renderItem function
const renderItemRE = ({ item }) => {
return (
<RenderItemRE
selectables={route.params?.selectables}
item={item}
navigation={navigation}
/>
);
};
And finally this is my component RenderItemRE
const RenderItemRE = ({ item, navigation, selectables }) => {
return (
<View style={styles.globalContainer}>
<TouchableOpacity
style={styles.touchable}
onPress={() => {
navigation.navigate(Routes.InfoEsercizio, {
id_ex: item.id,
nomeEx: item.nome,
});
}}
>
<View style={styles.container}>
<Image
indicator={Progress}
style={styles.img}
source={{
uri: item.galleria
? item.galleria[0]
? item.galleria[0]
: "logo"
: ApiConstants.DEFAULT_IMAGE,
}}
/>
<Text
style={[
customtext(DYNAMIC_FONTS_SIZE.FONT_SIZE_BIG).regular,
styles.nameStyle,
]}
>
{item.nome}
</Text>
</View>
</TouchableOpacity>
</View>
);
};
function arePropsEqual(prevProps, nextProps) {
return nextProps.item.id === prevProps.item.id;
}
export default memo(RenderItemRE, arePropsEqual);
This is a correct way to use memo in react native ? Or am I doing something wrong? I'm noticing that when the list gets bigger the rendering slows down. I was wondering what was the correct way to optimize. Thank you in advance

Why does FlatList scrolls to top when I select an item

I have a FlatList with TouchableOpacity items. When I try to select an item, the list scrolls to top.
This is the Collapsible content;
const setBrandSelected = index => {
var temp = JSON.parse(JSON.stringify(tempFilterOptions));
temp[0].enums[index].selected = !temp[0].enums[index].selected;
setTempFilterOptions(temp);
};
const FilterOptionBrand = () => {
const RenderBrand = ({ item, index }) => {
return (
<TouchableOpacity onPress={setBrandSelected.bind(null, index)}>
{item.selected && (
<View style={[styles.brandImage, styles.selectedBrandImage]}>
<Icon iconName={iconNames.Tick} />
</View>
)}
<FastImage source={{ uri: item.logo }} style={styles.brandImage} resizeMode={FastImage.resizeMode.contain} />
<Text style={styles.brandName}>{item.name}</Text>
</TouchableOpacity>
);
};
const BrandSeparator = () => <View style={styles.brandSeparator} />;
return (
<View style={styles.filterBrandMainContainer}>
<View style={styles.searchBrandContainer}>
<View style={styles.magnifyingGlassWrapper}>
<Icon iconName={iconNames.MagnifyingGlass} />
</View>
<TextInput style={styles.searchBrandInput} placeholder={searchBrandText} />
</View>
<FlatList
horizontal={true}
showsHorizontalScrollIndicator={false}
keyExtractor={(item, index) => String(item.id)}
style={styles.collapsibleContainer}
data={tempFilterOptions[0].enums}
renderItem={RenderBrand}
ItemSeparatorComponent={BrandSeparator}
></FlatList>
</View>
);
};
If you somehow stumble upon this question. The answer is wherever the problem occurs be it Header be it Footer. You should call the component function and not directly just type the function.
const Header = () => <View />
Correct usage;
ListHeaderComponent={Header()}
Incorrect usage;
ListHeaderComponent={Header}

function not passed with required variable (Expo/React Native)

I am trying to let allow the button to navigate me to the Home Screen after pressing, but it seems like the function does not have the variable 'navigation', how do I pass this variable to the function? Below is my code snippet
const LoggedInPage = props => {
return (
<View style={styles.container}>
<Text style={styles.loggedin}>Welcome: {props.name}</Text>
<Image style={styles.image} source={{ uri: props.photoUrl }} />
<TouchableOpacity style={styles.button2} onPress={() => this.props.navigation.navigate('Home')}>
<Text style={styles.buttonText}>Get Started!</Text>
</TouchableOpacity>
</View>
)
}
There is no this binding in functional component; you can use useNavigation hook to get the navigation prop.
Code:
const LoggedInPage = ({ name, photoUrl }) => {
const navigation = useNavigation();
return (
<View style={styles.container}>
<Text style={styles.loggedin}>Welcome: {name}</Text>
<Image style={styles.image} source={{ uri: photoUrl }} />
<TouchableOpacity style={styles.button2} onPress={() => navigation.navigate('Home')}>
<Text style={styles.buttonText}>Get Started!</Text>
</TouchableOpacity>
</View>
)
}