Hi I'm new to React Native and now I'm trying to create count up timer inside each Flat List row. The calculation based on data timestamp compare to current date. My countdown interval is working for each row but the problem now is performance. In Android sometimes is causing ANR (Application not responding).
Is there any suggestion that I can do to improve this code? Really appreciate for any kind help. Thank you.
import React, { useState, useEffect } from 'react'
import { View, Text, ActivityIndicator, Modal, FlatList } from 'react-native'
import Style from '../../../../constants/Style'
import AsyncStorage from '@react-native-community/async-storage'
import { API, Auth } from 'aws-amplify'
import AWSMQTTConnection from '../../../../common/AWSMQTTConnection'
import AsyncStorageConstants from '../../../../constants/AsyncStorageConstants'
var isLeaving = false
function EquipmentElement({ id, name, number, status, time, maintenanceCode }) {
const [hours, setHours] = useState('00')
const [minutes, setMinutes] = useState('00')
const [seconds, setSeconds] = useState('00')
var count = setInterval(() => {
var now = new Date().getTime()
var distance = now - time
var hours = Math.floor((distance % (1000 * 60 * 60 * 24)) / (1000 * 60 * 60))
var minutes = Math.floor((distance % (1000 * 60 * 60)) / (1000 * 60))
var seconds = Math.floor((distance % (1000 * 60)) / 1000)
var isHourLessThanTen = hours < 10
var isMinuteLessThanTen = minutes < 10
var isSecondLessThanTen = seconds < 10
if (isHourLessThanTen) {
hours = '0' + hours
}
if (isMinuteLessThanTen) {
minutes = '0' + minutes
}
if (isSecondLessThanTen) {
seconds = '0' + seconds
}
if ((status == 'Available' || status == undefined) || maintenanceCode == 1) {
clearInterval(count)
} else {
if (isLeaving) {
clearInterval(count)
} else {
setHours(hours)
setMinutes(minutes)
setSeconds(seconds)
}
}
}, 1000)
const setDurationValue = () => {
if (maintenanceCode == 1) {
<Text style={Style.value}>00:00:00</Text>
} else {
if (time != 0 || time != '' || time == undefined) {
return (<Text style={Style.value}>{hours}:{minutes}:{seconds}</Text>)
} else {
return (<Text style={Style.value}>00:00:00</Text>)
}
}
}
return (
<View style={Style.smartElementContainer}>
{setDurationValue()}
</View>
)
}
function Equipment() {
const [equipmentData, setEquipmentData] = useState([])
useEffect(() => {
isLeaving = false
getEquipmentList(false)
return function cleanup() {
console.log('unmounting...')
isLeaving = true
setEquipmentData([])
}
}, [])
const getEquipmentList = async (isMQTT) => {
try {
let propertyId = await AsyncStorage.getItem(AsyncStorageConstants.StorageConstants.CURRENT_PROPERTY_ID)
let apiName = 'DemoAsiaIoT'
let path = '/scdevice'
let request = {
headers: { Authorization: `Bearer ${(await Auth.currentSession()).getIdToken().getJwtToken()}` },
response: true,
queryStringParameters: {
propertyId: propertyId,
applicationId: 5
}
}
await API.get(apiName, path, request).then(response => {
var data = response.data.resultData
var devices = []
for (var i = 0; i < data.length; i++) {
var device = data[i]
devices.push({ id: device.deviceEUI, name: device.deviceName, number: '01', status: device.status, time: device.timestampStart, maintenanceCode: device.manualOverRide })
}
setEquipmentData(devices)
}).catch(error => {
console.log('Error from request - ', error.response)
})
} catch (err) {
console.log('error:', err)
}
}
return (
<View style={{ flex: 1 }}>
<FlatList
style={{
width: '100%',
marginTop: 10,
}}
showsVerticalScrollIndicator={false}
vertical={true}
data={equipmentData}
renderItem={({ item }) => <EquipmentElement id={item.id} name={item.name} number={item.number} status={item.status} time={item.time} maintenanceCode={item.maintenanceCode} />}
keyExtractor={item => item.id} />
</View>
)
}
export default Equipment
Few pointers on how you can optimize your code.
hours
, minutes
, and seconds
to show your time in your FlatList item. Those are the derivation from Date.now()
. You could check the code below to understand the concept. Simply create a helper function outside the scope of your component that returns your formatted time to show. Most of your performance can be solved if you do this.function calculateTimeLeft() {
const year = new Date().getFullYear();
const difference = +new Date(`${year}-10-1`) - +new Date();
let timeLeft = {};
if (difference > 0) {
timeLeft = {
days: Math.floor(difference / (1000 * 60 * 60 * 24)),
hours: Math.floor((difference / (1000 * 60 * 60)) % 24),
minutes: Math.floor((difference / 1000 / 60) % 60),
seconds: Math.floor((difference / 1000) % 60)
};
}
return timeLeft;
}
function FlatListItem() {
const [timeLeft, setTimeLeft] = React.useState(calculateTimeLeft());
React.useEffect(() => {
const id = setTimeout(() => {
setTimeLeft(calculateTimeLeft());
}, 1000);
return () => {
clearTimeout(id);
};
});
const timerComponents = Object.keys(timeLeft).map(interval => {
if (!timeLeft[interval]) {
return;
}
return (
<span>
{timeLeft[interval]} {interval}{" "}
</span>
);
});
I have no idea why are setting isLeaving
in your component. Remove it. It is of no use. clearTimeout
on your return statement from useEffect
.
Use your styles from StyleSheet.create
. Create a function keyExtractor
and place it outside your component. And pass item as a prop.
// outside the scope of your component
function keyExtractor(item) {
return item.id;
}
<FlatList
style={styles.flatlist} // place your styles in `StyleSheet.create`
showsVerticalScrollIndicator={false}
vertical={true}
data={equipmentData}
renderItem={EquipmentElement} // pass the item as a prop
keyExtractor={keyExtractor}
/>