I’ve two parts: AllBuyersTab and TicketCheckedInTab. The TicketCheckedInTab works fantastic, however I’m experiencing scroll lag in AllBuyersTab. Can anybody recommend an answer?
I attempted numerous options by utilizing totally different props accessible for FlatList from the React Native docs, however they didn’t assist. Generally I received clean row points, which I mounted by assigning a hard and fast peak and utilizing getItemLayout. Nevertheless, the scrolling challenge nonetheless persists — and surprisingly, it solely occurs in AllBuyersTab, not in TicketCheckedInTab
import React, { useEffect, useState } from 'react';
import {
CHECKIN_MESSAGES,
ERROR_MESSAGE_OVERRIDE,
EVENT_ERROR_MESSAGES,
} from '@/constants/messages';
import {
View,
Textual content,
StyleSheet,
TouchableOpacity,
ScrollView,
FlatList,
Modal,
ActivityIndicator,
} from 'react-native';
import { router, useLocalSearchParams } from 'expo-router';
import { useDataProvider } from '@/supplier/DataProvider';
import {
getAttendeesTicketStatus,
updateCancelledAttendeeTicketStatus,
incrementCheckedCount,
cleanLocalDB,
getLastSyncAt,
insertAttendee,
} from '@/db/sqlite';
import EmptyList from '@/parts/EmptyList';
import checkedInDateFormat from '@/utils/checked-in-date-format';
import { useSQLiteContext } from 'expo-sqlite';
import RenderAttendeesStatus from '@/parts/RenderAttendeesStatus';
import { Attendee, AttendeeStatus, TicketStatus } from '@/varieties/varieties';
import { manualCheckin } from '@/companies/api';
import { useBuyers } from '@/hooks/useBuyers';
import eventBus from '@/eventBus';
import { useQueryClient } from '@tanstack/react-query';
const ITEM_HEIGHT = 100;
interface AllBuyersCard {
attendee: any;
errorMessage?: any;
isLoading: any;
handleTryAgain: (orderId: string, datetimeId: string) => void;
handleManualCheckIn: (orderId: string, datetimeId: string) => void;
}
operate AllBuyersCard({
attendee,
errorMessage,
isLoading,
handleTryAgain,
handleManualCheckIn,
}: AllBuyersCard) {
console.log("Rendered:", attendee.orderid);
const [modalVisible, setModalVisible] = useState(false);
const showConfirmation = () => {
setModalVisible(true);
};
const handleConfirm = () => {
setModalVisible(false);
handleManualCheckIn(attendee.orderid, attendee.datetime_id);
};
const handleCancel = () => {
setModalVisible(false);
};
return (
{attendee.fname} {attendee.lname}
OrderId #{attendee?.orderid?.slice(-6)}
Qty {attendee.total_qty}
Verify Examine-In
Are you certain you wish to test in {attendee.fname}{' '}
{attendee.lname} with Order #
{attendee?.orderid?.slice(-6)}?
Cancel
Sure
);
}
export default operate AllBuyersTab() {
const params = useLocalSearchParams();
const db = useSQLiteContext(); /* Database reference */
const queryClient = useQueryClient();
const [isLoading, setIsLoading] = React.useState({});
/* state to repair overlapping challenge in rendering attendee record */
const [initialRender, setInitialRender] = React.useState(true);
/* finish of overlapping state */
const [errorMessage, setErrorMessage] = React.useState({});
const {
eventData,
eventLabel,
datetimeId,
refetch,
searchQuery,
isOnline,
} = useDataProvider();
const {
patrons,
loading,
hasMore,
loadMore,
refetch: localRefetch,
} = useBuyers(db, eventLabel as string, datetimeId as string, searchQuery);
useEffect(() => {
const listener = () => {
refetch();
localRefetch();
};
eventBus.on('ticketCheckedIn', listener);
eventBus.on('reset', listener);
return () => {
eventBus.off('ticketCheckedIn', listener);
eventBus.off('reset', listener);
};
}, []);
const handleManualCheckIn = async (orderId: string, datetimeId: string) => {
attempt {
setIsLoading((prev) => ({ ...prev, [orderId]: true }));
setErrorMessage((prev) => ({ ...prev, [orderId]: null }));
// Examine if ticket exists domestically
const existingTicket = await getAttendeesTicketStatus(
db,
eventData.occasions.event_id,
orderId,
datetimeId
);
const timestamp = await getLastSyncAt(
db,
eventLabel as string,
datetimeId,
1
);
const eventId = eventData.occasions.event_id;
if (isOnline) {
if (existingTicket) {
const checkinResult = await manualCheckin({
datetimeId,
orderId,
eventId,
lastSyncTime: timestamp?.lastSyncAt as string,
});
// if refunded_on has date which means we have to save checked_date null
// sync will likely be true that's 1
/*
* checked Date
* if there's checked_date and it standing chk_status not already "1"
* */
// const eventId = eventData.occasions.event_id;
const basePayload = {
eventId: eventId,
orderId,
datetimeId,
sync: 1,
checkedInDate: checkinResult?.checked_date,
refunded_on: checkinResult?.refunded_on,
canceled_on: checkinResult?.canceled_on,
message: checkinResult?.message,
};
if (
checkinResult.att_detail.standing ===
AttendeeStatus.CheckedIn
) {
const payload = {
...basePayload,
chk_status: TicketStatus.CHECKED,
};
await updateCancelledAttendeeTicketStatus(db, payload);
/*
* Notice: In case of refunded and canceled customers, no have to increment the order checked
* */
// Increment checked
await incrementCheckedCount(db, eventId, datetimeId);
} else if (
checkinResult.att_detail.standing ===
AttendeeStatus.Refunded
) {
const payload = {
...basePayload,
chk_status: TicketStatus.UNCHECKED,
};
await updateCancelledAttendeeTicketStatus(db, payload);
} else if (
checkinResult.att_detail.standing ===
AttendeeStatus.Canceled
) {
const payload = {
...basePayload,
chk_status: TicketStatus.UNCHECKED,
};
await updateCancelledAttendeeTicketStatus(db, payload);
}
// Emit Occasion in order that we will at all times have TicketCheckedInTab refresh
eventBus.emit('ticketCheckedIn');
refetch();
localRefetch();
router.exchange({
pathname: '/(tabs)/checked-in',
params: {
knowledge: JSON.stringify(checkinResult),
// Under parameters used to redirect the person again on the web page kind the place they got here from
datetimeId: params.datetime_id,
dateId: params.date_id,
eventLabel: params.event_label,
},
});
} else {
// if not ticket not exist domestically however on-line
const checkinResult = await manualCheckin({
datetimeId,
orderId,
eventId,
lastSyncTime: timestamp?.lastSyncAt as string,
});
const ALREADY_CHECKED_MESSAGE =
CHECKIN_MESSAGES.ALREADY_CHECKED_IN_MESSAGE;
/* Since we have no different approach to make sure that the beneath block run solely as soon as
* we're evaluating the message, since we have to increment the checked rely solely as soon as, not each time.
* */
if (
checkinResult.checked_date &&
checkinResult.message !== ALREADY_CHECKED_MESSAGE
) {
/*
* Notice: Do i want to make use of transaction right here give it some thought.
* */
await incrementCheckedCount(db, eventId, datetimeId);
const { ticket_quantity: total_qty, ...relaxation } =
checkinResult.att_detail;
const attendee: Attendee = {
...relaxation,
total_qty,
refund_status: relaxation.refund_status ?? null,
};
await insertAttendee(
db,
attendee,
checkinResult.occasions
);
}
eventBus.emit('ticketCheckedIn');
refetch();
router.exchange({
pathname: '/(tabs)/checked-in',
params: {
knowledge: JSON.stringify(checkinResult),
// Under parameters used to redirect the person again on the web page kind the place they got here from
datetimeId: params.datetime_id,
dateId: params.date_id,
eventLabel: params.event_label,
},
});
}
} else {
if (!existingTicket) {
throw new Error('Ticket not discovered domestically.');
}
let localDbResponse = null;
const eventId = eventData.occasions.event_id;
const basePayload = {
eventId: eventId,
orderId,
datetimeId,
sync: 0,
};
if (existingTicket.standing === AttendeeStatus.CheckedIn) {
const payload = {
...basePayload,
checkedInDate: checkedInDateFormat(),
chk_status: TicketStatus.CHECKED,
};
localDbResponse = await updateCancelledAttendeeTicketStatus(
db,
payload
);
/*
* Notice: In case of refunded and canceled customers, no have to increment the order checked
* */
// Increment checked
await incrementCheckedCount(db, eventId, datetimeId);
} else if (existingTicket.standing === AttendeeStatus.Refunded) {
/* Refund case */
const refundMessage = CHECKIN_MESSAGES.REFUND_MESSAGE;
const payload = {
...basePayload,
checkedInDate: null,
refunded_on: existingTicket?.refunded_on,
canceled_on: null,
chk_status: TicketStatus.UNCHECKED,
message: refundMessage,
};
localDbResponse = await updateCancelledAttendeeTicketStatus(
db,
payload
);
} else if (existingTicket.standing === AttendeeStatus.Canceled) {
/*
* canceled
* */
const cancelledMessage = CHECKIN_MESSAGES.CANCELLED_MESSAGE;
const payload = {
...basePayload,
checkedInDate: null,
refunded_on: null,
canceled_on: existingTicket?.canceled_on,
chk_status: TicketStatus.UNCHECKED,
message: cancelledMessage,
};
localDbResponse = await updateCancelledAttendeeTicketStatus(
db,
payload
);
}
/*
* checked Date
* if there's checked_date and it standing chk_status not already "1"
* */
eventBus.emit('ticketCheckedIn');
refetch();
localRefetch();
router.exchange({
pathname: '/(tabs)/checked-in',
params: {
knowledge: JSON.stringify(localDbResponse),
// Under parameters used to redirect the person again on the web page kind the place they got here from
datetimeId: params.datetime_id,
dateId: params.date_id,
eventLabel: params.event_label,
},
});
}
} catch (error: any) {
console.log(error.message);
const DELETE_MESSAGE = EVENT_ERROR_MESSAGES.DELETE_MESSAGE;
const REJECTED_MESSAGE = EVENT_ERROR_MESSAGES.REJECTED_MESSAGE;
const EXPIRED_MESSAGE = EVENT_ERROR_MESSAGES.EXPIRED_MESSAGE;
if (error?.message?.consists of(DELETE_MESSAGE)) {
await cleanLocalDB(
db,
eventLabel as string,
datetimeId as string
);
await queryClient.invalidateQueries({ queryKey: ['evList'] });
await queryClient.invalidateQueries({
queryKey: ['eventListProfile'],
});
alert(ERROR_MESSAGE_OVERRIDE.EVENT_MESSAGE);
router.exchange("https://stackoverflow.com/");
} else if (error?.message?.consists of(REJECTED_MESSAGE)) {
await cleanLocalDB(
db,
eventLabel as string,
datetimeId as string
);
await queryClient.invalidateQueries({ queryKey: ['evList'] });
await queryClient.invalidateQueries({
queryKey: ['eventListProfile'],
});
await queryClient.invalidateQueries({ queryKey: ['eventFV'] });
alert(REJECTED_MESSAGE);
router.exchange("https://stackoverflow.com/");
} else if (error?.message?.consists of(EXPIRED_MESSAGE)) {
await cleanLocalDB(
db,
eventLabel as string,
datetimeId as string
);
await queryClient.invalidateQueries({ queryKey: ['evList'] });
await queryClient.invalidateQueries({ queryKey: ['eventFV'] });
await queryClient.invalidateQueries({
queryKey: ['eventListProfile'],
});
alert(EXPIRED_MESSAGE);
router.exchange("https://stackoverflow.com/");
}
setErrorMessage((prev) => ({ ...prev, [orderId]: error.message }));
} lastly {
setIsLoading((prev) => ({ ...prev, [orderId]: false }));
}
};
const handleTryAgain = async (orderId: string, datetimeId: string) => {
await handleManualCheckIn(orderId, datetimeId);
};
return (
}
keyExtractor={(merchandise) => merchandise?.orderid?.toString()}
renderItem={({ merchandise }) => (
)}
showsVerticalScrollIndicator={false}
onEndReached={loadMore}
onEndReachedThreshold={1}
initialNumToRender={10}
maxToRenderPerBatch={30}
getItemLayout={(knowledge, index) => ({
size: ITEM_HEIGHT, // peak of every merchandise
offset: ITEM_HEIGHT * index,
index,
})}
windowSize={10}
removeClippedSubviews
ListFooterComponent={
loading ? : null
}
/>
);
}
const kinds = StyleSheet.create({
list_org_wrp_lastrow: {
paddingBottom: 70,
},
list_org_wrp: {
// peak: ITEM_HEIGHT,
backgroundColor: '#fff',
marginTop: 15,
padding: 11,
boxShadow: '0 7 10 rgba(0, 0, 0, 0.03)',
borderRadius: 5,
borderWidth: 1,
borderColor: 'rgba(0, 0, 0, 0.125)',
},
nme_byr: {
fontSize: 16,
fontWeight: 600,
marginBottom: 4,
},
list_orderbyr: {
fontSize: 15,
fontWeight: 400,
marginBottom: 2,
coloration: ' #808080',
},
list_qtybyr: {
fontSize: 15,
fontWeight: 500,
marginBottom: 2,
coloration: '#4ba03e',
},
btn_checkin_btn_trng: {
peak: '100%',
alignItems: 'middle',
justifyContent: 'middle',
marginRight: 4,
},
checkinbtnmn: {
padding: 10,
borderRadius: 3,
},
chkedin_btntxtss: {
fontWeight: 600,
},
});
import React, { useEffect } from 'react';
import {
View,
Textual content,
ScrollView,
StyleSheet,
ActivityIndicator,
} from 'react-native';
import { useDataProvider } from '@/supplier/DataProvider';
import { FlatList } from 'react-native';
import { useSQLiteContext } from 'expo-sqlite';
import { useCheckedIn } from '@/hooks/useCheckedIn';
import EmptyList from '@/parts/EmptyList';
import { useFocusEffect } from 'expo-router';
import eventBus from '@/eventBus';
import removeSecondsFromDateString from '@/utils/removeSecondsFromDateString';
const ITEM_HEIGHT = 100;
interface Attendee {
orderid: string;
fname: string;
lname: string;
total_qty: string;
chk_status: string;
checked_date: string;
}
operate TicketCheckedInCard({ attendee }: { attendee: Attendee }) {
console.log("Rendered:", attendee.orderid);
return (
{attendee.fname} {attendee.lname}
OrderId #{attendee?.orderid?.slice(-6)}
Qty {attendee.total_qty}
Checked
{attendee.checked_date && (
{/*{attendee.checked_date}*/}
{removeSecondsFromDateString(attendee.checked_date)}
)}
);
}
export default operate TicketCheckedInTab() {
const db = useSQLiteContext();
const {
eventData,
eventLabel,
datetimeId,
refetch,
searchQuery,
setSearchQuery,
isOnline,
} = useDataProvider();
const {
patrons,
loading,
hasMore,
loadMore,
refetch: localRefetch,
} = useCheckedIn(
db,
eventLabel as string,
datetimeId as string,
searchQuery
);
useFocusEffect(
React.useCallback(() => {
// refetch(); // reload patrons at any time when tab is concentrated
// // if(searchQuery) {
// setSearchQuery('') // we have to set the search right here everytime we swap to checked-in tab, for the reason that search is world if we do not do that then it's going to replicate the final searched worth in checked-in tab
// }
localRefetch();
}, [eventLabel, datetimeId])
);
useEffect(() => {
const listener = () => {
// refetch();
localRefetch();
};
eventBus.on('ticketCheckedIn', listener);
eventBus.on('reset',listener);
return () => {
eventBus.off('ticketCheckedIn', listener);
eventBus.off('reset',listener);
};
}, []);
/* state to repair overlapping challenge in rendering attendee record */
// const [initialRender, setInitialRender] = React.useState(true);
/* finish of overlapping state */
// const CheckedInConfirmationList = eventData?.attendees?.filter(
// (attendee: Attendee) => attendee?.chk_status.toString() === '1'
// );
return (
}
keyExtractor={(merchandise) => merchandise?.orderid?.toString()}
renderItem={({ merchandise }) => }
showsVerticalScrollIndicator={false}
onEndReached={loadMore}
onEndReachedThreshold={1}
initialNumToRender={10}
// initialNumToRender={patrons.size}
getItemLayout={(knowledge, index) => ({
size: ITEM_HEIGHT, // peak of every merchandise
offset: ITEM_HEIGHT * index,
index,
})}
maxToRenderPerBatch={30}
windowSize={10}
removeClippedSubviews={false}
ListFooterComponent={
loading ? : null
}
/>
//
// {CheckedInConfirmationList?.map((merchandise) => (
//
// ))}
//
// {/* Deliberately left paddingBottom in order that QRcode scanner is not going to overlay on occasion record */}
//
//
//
);
}
const kinds = StyleSheet.create({
list_org_wrp_lastrow: {
paddingBottom: 70,
},
list_org_wrp: {
backgroundColor: '#fff',
marginTop: 15,
padding: 11,
boxShadow: '0 7 10 rgba(0, 0, 0, 0.03)',
borderRadius: 5,
borderWidth: 1,
borderColor: 'rgba(0, 0, 0, 0.125)',
},
nme_byr: {
fontSize: 16,
fontWeight: 600,
marginBottom: 4,
},
list_orderbyr: {
fontSize: 15,
fontWeight: 400,
marginBottom: 2,
coloration: ' #808080',
},
list_qtybyr: {
fontSize: 15,
fontWeight: 500,
marginBottom: 2,
coloration: '#4ba03e',
},
});