I just recently learned how to use useCallback() hook. Right now I have an application to render a component called "Events" in the FlatList React Native component. I looked up posts on how to use useCallback() in a FlatList component and I implemented as I have my code below. However, I still received this warning message: VirtualizedList: You have a large list that is slow to update - make sure your renderItem function renders components that follow React performance best practices like PureComponent, shouldComponentUpdate, etc. Object { "contentLength": 3362, "dt": 307776, "prevDt": 6216,}. I intepreted that if I received this warning message, it is very likely that I did not implement this hook right. How can I implement it correctly? I appreciate all helps I can get to help me to learn how to use this hook efficiently. Thank you!
Here is my HomeScreen component in which I have the FlatList component:
import React, { useState, useRef, useEffect, useCallback } from 'react'
import { FlatList, StyleSheet, View, Text, TouchableOpacity, Image, RefreshControl } from 'react-native'
import { auth } from '../Firebase'
import Event from "./Event";
import { getFeed, getEventsByUserName } from "./services/EventsServices";
import useStore from "../store";
import { container } from './style';
import { useNavigation } from "@react-navigation/core";
const HomeScreen = (props) => {
const [eventList, setEventList] = useState([]);
const [refresh, setRefresh] = useState(false);
const navigation = useNavigation();
const refresh_feed = () => {
if (props.route.params.isMyFeed) {
getEventsByUserName(useStore.getState().userName).then(events => {
setEventList(events);
}).catch(error => console.log(error))
}
else {
getFeed().then(events => {
setEventList(events);
}).catch(error => console.log(error))
}
}
// synchronize the local refresh state with the global refresh state
useEffect(() => {
useStore.setState({
refresh: false
})
refresh_feed();
}, [refresh])
useEffect(() => {
useStore.subscribe(() => setRefresh(true), state => state.refresh)
}, [setRefresh])
const handleSignOut = () => {
useStore.setState({ loggedIn: false, refresh: false, userName: null, userId: null, events: [] })
auth
.signOut()
.then(() => {
navigation.replace("Login")
})
.catch(error => alert(error.message))
}
const renderItem = useCallback(({ item, index }) => {
return (
<View key={index}>
<Event route={{ params: { item, index, user: item.userName, isMyFeed: props.route.params.isMyFeed } }} name="Event" navigation={navigation} />
</View>
)
},[])
return (
<View style={container.container}>
<FlatList
refreshControl={
<RefreshControl
refreshing={useStore.getState().refresh}
onRefresh={() => {
refresh_feed();
}}
/>
}
viewabilityConfig={{
waitForInteraction: false,
viewAreaCoveragePercentThreshold: 70,
}}
data={eventList}
renderItem={renderItem}
numColumns={1}
horizontal={false}
keyExtractor={(item) => item.media_id}
contentContainerStyle={{
padding: 20,
paddingTop: 42
}}
/>
</View>
)
}
export default HomeScreen
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center'
},
event: {
backgroundColor: '#F0F8FF',
width: '100%',
padding: 15,
borderRadius: 10,
borderColor: '#0782F9',
alignItems: 'center',
marginTop: 40,
},
eventText: {
color: 'black',
fontWeight: '700',
fontSize: 16,
},
button: {
backgroundColor: '#0782F9',
width: 'auto',
padding: 15,
borderRadius: 10,
alignItems: 'center',
marginTop: 40,
},
buttonText: {
color: 'white',
fontWeight: '700',
fontSize: 16,
},
separatorStyle: {
height: 0.5,
width: '100%',
backgroundColor: '#C8C8C8',
},
avatarImage: {
width: 70,
height: 70,
borderRadius: 70
},
eventImage: {
}
})
Here is my Event component:
import { useEffect, useState, memo } from "react";
import {ImageBackground, View, Text, TouchableOpacity, Image} from "react-native";
import { utils, container, text } from "./style";
import { Feather, FontAwesome5 } from "@expo/vector-icons";
import useStore from "../store";
import { getUserByUserName } from "./services/userServices";
const Event = (props) => {
const [event, setEvent] = useState(props.route.params.item);
const [eventUser, setEventUser] = useState("");
const [eventUserSet, setEventUserSet] = useState(false);
useEffect(() => {
if (event.hasOwnProperty("userName")) {
getUserByUserName(event.userName).then(querySnapShot => {
querySnapShot.forEach(doc => {
setEventUser(doc.data());
});
});
setEventUserSet(true);
}
else {
setEventUser(event.user_id);
setEventUserSet(true);
}
}, [])
if (props.route.params.item.media !== undefined && eventUserSet) {
return (
<View>
<EventCard params={{ passedRoute: props.route, event: event, eventUser: eventUser, isMyFeed: props.route.params.isMyFeed }} navigation={props.navigation} />
</View>
)
} else {
return (
<View>
</View>
)
}
}
const EventCard = (props) => {
const openDetailedView = () => {
props.navigation.navigate('Details', {
passedParams: {passedRoute: props.params.passedRoute, event: props.params.event, eventUser: props.params.eventUser, isMyFeed: props.params.isMyFeed},
})
}
const avatarImage = props.params.eventUser.image === 'default' ?
(
<FontAwesome5
style={utils.profileImageSmall}
name={'user-circle'} size={70} color={'black'} />
)
:
(
<Image
style={utils.profileImageSmall}
source={{ uri: props.params.eventUser.image }}
/>
)
return(
//real JSX returned
<View
style = {{ marginBottom:20, borderRadius: 16, backgroundColor:"gray"}}>
<ImageBackground
source={{ uri: props.params.event.media}}
style = {{
flexDirection:'row',
padding: 10,
paddingTop:30,
paddingBottom: 20,
marginBottom:20,
height:180,
borderRadius: 16,
flex : 1,
resizeMode: 'cover'}}>
{/*//Event display*/}
<TouchableOpacity
//TODO::change the width dynamically
style = {{
flexDirection:'row',
height:130,
width:330,
backgroundColor: 'rgba(255, 255, 255, 0.73)',
padding: 20,
color: '#fff',
fontSize: 26
}}
onPress={openDetailedView}
>
<View>
{avatarImage}
</View>
<View>
<Text style = {{fontSize: 22, fontWeight:'700',width:110,height: 30, display: "flex"}}>{props.params.event.eventTitle}</Text>
<Text style = {{fontSize: 14, opacity: .7, width:90, height: 40, display:"flex"}}>{props.params.event.eventDescription}</Text>
<Text style = {{fontSize: 12, opacity: .8, color : '#0099cc'}}> {"End date: "} {props.params.event.eventEndDate}</Text>
<Text style = {{fontSize: 12, opacity: .8, color : '#0099cc'}}>
{" Loc: "} {props.params.event.eventLocation}
</Text>
</View>
{/*//the place of it is moving with content*/}
<View style={[utils.padding10, container.horizontal,{
paddingTop:35
}]}>
<Feather style={utils.margin15Left} name={"message-square"} size={50} color={"black"}
onPress={() => props.navigation.navigate("Comment", {
eventId: props.params.passedRoute.params.item.id,
user: useStore.getState().userName
})} />
</View>
</TouchableOpacity>
</ImageBackground>
</View>
)
}
export default memo(Event);