I'm making a notes app in React Native and trying to make it so I can click on a note in a FlatList to edit it. I'm using react-router-native for this. I get an Error when clicking on any FlatList item. I know that this error has been asked on stack overflow before but the answers are all for class components, whereas I'm using functional components.
Error: Invalid hook call. Hooks can only be called inside of the body of a function component.
import { FlatList, Pressable, StyleSheet, View } from "react-native"
import { useNavigate } from "react-router-native"
import theme from "../theme"
import Text from "./Text"
const styles = StyleSheet.create({
separator: {
height: 10,
backgroundColor: theme.colors.background,
},
item: {
padding: 8,
backgroundColor: "white",
},
})
const ItemSeparator = () => <View style={styles.separator} />
const renderItem = ({ item }) => (
<View style={styles.item}>
<Pressable onPress={() => useNavigate(`/${item.id}`)}>
<Text fontWeight="bold" fontSize="subheading">
{item.title}
</Text>
<Text>{item.body}</Text>
</Pressable>
</View>
)
const NoteList = ({ notes }) => {
return (
<FlatList
data={notes}
ItemSeparatorComponent={ItemSeparator}
renderItem={renderItem}
keyExtractor={(item) => item.id}
/>
)
}
CodePudding user response:
useNavigate
is a React hook and can only be called by a React function component or other custom React hook. It cannot be called in nested functions/callbacks.
Move the useNavigate
hook call to the NoteList
component and refactor the renderItem
callback to curry a passed navigate
function.
const ItemSeparator = () => <View style={styles.separator} />;
const renderItem = (navigate) => ({ item }) => (
<View style={styles.item}>
<Pressable onPress={() => navigate(`/${item.id}`)}>
<Text fontWeight="bold" fontSize="subheading">
{item.title}
</Text>
<Text>{item.body}</Text>
</Pressable>
</View>
);
const NoteList = ({ notes }) => {
const navigate = useNavigate(); // <-- hook called in React function
return (
<FlatList
data={notes}
ItemSeparatorComponent={ItemSeparator}
renderItem={renderItem(navigate)} // <-- pass navigate
keyExtractor={(item) => item.id}
/>
);
};
Alternatively you could move the renderItem
function declaration into the NoteList
component so the navigate
function is just closed over in callback scope.
const ItemSeparator = () => <View style={styles.separator} />;
const NoteList = ({ notes }) => {
const navigate = useNavigate();
const renderItem = ({ item }) => (
<View style={styles.item}>
<Pressable onPress={() => navigate(`/${item.id}`)}>
<Text fontWeight="bold" fontSize="subheading">
{item.title}
</Text>
<Text>{item.body}</Text>
</Pressable>
</View>
);
return (
<FlatList
data={notes}
ItemSeparatorComponent={ItemSeparator}
renderItem={renderItem}
keyExtractor={(item) => item.id}
/>
);
};