So I am using Expo and react-navigation
along with ListItem
from react-native-elements
to create a FlatList
that renders the names of doctors and I want the user to be able to click on a "Know More" button inside the ListItem
which then navigates to a new screen containing information about the doctor. I am able to render the FlatList
and have added an Alert
on clicking the "Know more" button for testing purposes but the weird bug I get is that I have a total of 8 ListItems
and the Alert
shows up only on the last (i.e. the 8th) ListItem
and does not work on the other elements.
Here's all my code from FindDocScreen
:
import React from "react";
import {
View,
Text,
StyleSheet,
StatusBar,
FlatList,
TouchableOpacity,
Alert,
} from "react-native";
import MyHeader from "../components/MyHeader";
import { ListItem, Avatar } from "@rneui/themed";
import { LinearGradient } from "expo-linear-gradient";
import { RFValue } from "react-native-responsive-fontsize";
const list = [
{
name: "Dr. Ashok Mahato",
avatar_url: "https://api.lorem.space/image/face",
subtitle: "General Medicine",
rating: 4.2,
},
{
name: "Dr. Tapan Kumar",
avatar_url: "https://api.lorem.space/image/face",
subtitle: "Cardiologist",
rating: 4.5,
},
{
name: "Dr. Reyaz Ahmed",
avatar_url: "https://api.lorem.space/image/face",
subtitle: "Neurologist",
rating: 3.9,
},
{
name: "Dr. Poonam Singh",
avatar_url: "https://api.lorem.space/image/face",
subtitle: "Opthalmologist",
rating: 4,
},
{
name: "Dr. Nikhilesh Kundu",
avatar_url: "https://api.lorem.space/image/face",
subtitle: "Dermatologist",
rating: 3.8,
},
{
name: "Dr. Manoj Kumar",
avatar_url: "https://api.lorem.space/image/face",
subtitle: "Psychiatrist",
rating: 4.1,
},
{
name: "Dr. Abhik Chatterjee",
avatar_url: "https://api.lorem.space/image/face",
subtitle: "Surgeon",
rating: 4.6,
},
{
name: "Dr. Ranjan Kumar",
avatar_url: "https://api.lorem.space/image/face",
subtitle: "Gynecologist",
rating: 3.8,
},
];
magicWork = () => {
};
const keyExtractor = (item, index) => index.toString();
const renderItem = ({ item, i, navigation }) => {
return (
<ListItem
key={i}
linearGradientProps={{
colors: ["#6b79e4", "#7380f0"],
start: { x: 1, y: 0 },
end: { x: 0.2, y: 0 },
}}
ViewComponent={LinearGradient}
style={styles.doc}
containerStyle={{ borderRadius: 20 }}
bottomDivider
>
<Avatar
rounded
title={item.name[0]}
source={item.avatar_url && { uri: item.avatar_url }}
/>
<ListItem.Content>
<ListItem.Title style={{ color: "white", fontWeight: "bold" }}>
{item.name}
</ListItem.Title>
<ListItem.Subtitle style={{ color: "white" }}>
{item.subtitle} - {item.rating} ★
</ListItem.Subtitle>
</ListItem.Content>
<ListItem.Content right={true}>
<TouchableOpacity
style={styles.button}
onPress={() => {
//navigation.replace("Details", { data: item });
Alert.alert("Click", "Clicked button");
}}
>
<Text style={styles.buttonText}>Know More</Text>
</TouchableOpacity>
</ListItem.Content>
</ListItem>
);
};
export default class FindDocScreen extends React.Component {
render() {
return (
<View style={styles.container}>
<StatusBar
barStyle="dark-content"
hidden={false}
backgroundColor="#f8f8f8"
translucent={true}
/>
<MyHeader />
<Text style={styles.text}>Top doctors ★</Text>
<FlatList
contentContainerStyle={{ paddingBottom: RFValue(60) }}
keyExtractor={keyExtractor}
data={list}
renderItem={renderItem}
/>
</View>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: "#e9ecf7",
},
heading: {
fontSize: RFValue(27),
fontWeight: "bold",
color: "#fff",
alignSelf: "center",
},
text: {
fontSize: RFValue(30),
marginLeft: RFValue(15),
marginTop: RFValue(20),
color: "#000",
fontWeight: "bold",
},
doc: {
marginTop: RFValue(60),
width: "88%",
height: RFValue(18),
alignSelf: "center",
},
button: {
width: RFValue(85),
height: RFValue(30),
backgroundColor: "#e9ecf7",
borderRadius: 50,
alignSelf: "center",
textAlign: "center",
justifyContent: "center",
},
buttonText: {
fontSize: RFValue(11),
alignSelf: "center",
color: "#000",
},
});
The implementation of your renderItem
function seems wrong. The signature of this function is documented in the documentation and is as follows:
renderItem({ item, index, separators });
There is no navigation
object that is passed to this function. Furthermore, if you destructure properties from an object, then the defined names of the corresponding keys must match. It is called index
not i
. Thus, there might be an issue with the key
property that you are passing to ListItem
.
Hence,
const renderItem = ({ item, i, navigation })
is invalid.
We need to change it to the following.
const renderItem = ({ item, index })
Furthermore, it seems odd that your using a key
prop and a keyExtractor
at the same time. It might be worth a try to choose only one of these options.
The navigation
object will be passed to screens that are defined as components inside of a navigator. This is documented here in the official documentation.
Thus, we have a few options here to make this object accessible to the renderItem
function.
If FindDocScreen
is a screen defined in a navigator, then we can pass it to this function directly.
renderItem={({item, index}) => renderItem(item, index, this.props.navigation)}
You need to change the signature of your renderItem
function to the following.
const renderItem = (item, index, navigation ) => {
....
}
If FindDocScreen
is not a screen in the navigator, then you can access the navigation object anyway.
For react functional components
, this is easy using the useNavigation
hook. Since your component does not provide any special reason for being a class component, I would convert it to a functional component and would use this hook instead.
export default function FindDocScreen(props) {
const navigation = useNavigation();
return (
<View style={styles.container}>
<StatusBar
barStyle="dark-content"
hidden={false}
backgroundColor="#f8f8f8"
translucent={true}
/>
<MyHeader />
<Text style={styles.text}>Top doctors ★</Text>
<FlatList
contentContainerStyle={{ paddingBottom: RFValue(60) }}
keyExtractor={keyExtractor}
data={list}
renderItem={renderItem={({item, index}) => renderItem(item, index, navigation)}}
/>
</View>
);
}
}