Basically the title, there's a book search in the app I'm working on. I can display it completely as expected in the web view (displaying with a Flatlist), but when I load up on my emulator or phone (both android and ios), even though I get the data back when I search it won't update to display it.
const Search_Existing_Book = ({ navigation }) => {
const [searchQuery, setSearchQuery] = useState("");
const [searchResults, setSearchResults] = useState([]);
const [selectedBook, setSelectedBook] = useState({});
const [title, setTitle] = useState("");
const [authors, setAuthors] = useState("");
const [description, setDescription] = useState("");
const [imgUrl, setImgUrl] = useState("");
const [page, setPage] = useState(1);
const [hasMore, setHasMore] = useState(true);
const [totalItems, setTotalItems] = useState(0);
let totalPages = Math.ceil(totalItems / 20);
const api = process.env.GOOGLE_BOOKS_API_KEY;
const handleSearch = async () => {
try {
const response = await fetch(
`https://www.googleapis.com/books/v1/volumes?q=${searchQuery}&startIndex=${
page * 20
}&maxResults=20&key=${api}`
);
const data = await response.json();
console.log(data);
setTotalItems(data.totalItems);
setSearchResults(data.items);
if (data.totalItems <= page * 20 + 20) {
setHasMore(false);
}
} catch (error) {
console.error(error);
}
};
useEffect(() => {
handleSearch();
}, [page]);
const handleSelectBook = (book) => {
setSelectedBook(book);
setTitle(book.volumeInfo.title);
setAuthors(book.volumeInfo.authors?.join(", ") || "");
setDescription(book.volumeInfo.description);
setImgUrl(
book.volumeInfo.imageLinks
? book.volumeInfo.imageLinks.smallThumbnail
: "https://png.pngtree.com/png-vector/20221125/ourmid/pngtree-no-image-available-icon-flatvector-illustration-pic-design-profile-vector-png-image_40966566.jpg"
);
};
useEffect(() => {
if (title || authors || description) {
navigation.navigate("CreateListing", {
currTitle: title,
authors: authors,
currDescription: description,
imgUrl: imgUrl,
navigation: navigation,
});
}
}, [title, authors, description, imgUrl]);
const handlePageChange = (newPage) => {
setPage(newPage);
handleSearch();
};
useEffect(() => {
handleSearch();
}, [page]);
return (
<View style={styles.wrapperContainer}>
<Text> ADD A NEW BOOK! </Text>
<Text> Search for title here: </Text>
<SearchBar
placeholder="Search for book here.."
onChangeText={setSearchQuery}
value={searchQuery}
onSubmitEditing={handleSearch}
/>
<View style={styles.marginBottom}>
<View style={styles.container}>
<FlatList
data={searchResults}
keyExtractor={(item) =>
`${item.title}-${item.author}-${Math.random()}`
}
extraData={totalItems}
renderItem={({ item }) => (
<View style={styles.item}>
<TouchableOpacity onPress={() => handleSelectBook(item)}>
<View
style={[
styles.container,
{
borderTopWidth: 1,
borderBottomWidth: 1,
borderColor: "black",
},
]}
>
<Text style={styles.bookTitle}>
{item.volumeInfo.title}
</Text>
<Text style={styles.author}>
Written by{" "}
{item.volumeInfo.authors &&
item.volumeInfo.authors.join(", ")}
</Text>
<Image
source={
item.volumeInfo.imageLinks !== undefined
? { uri: item.volumeInfo.imageLinks.smallThumbnail }
: {
uri: "https://png.pngtree.com/png-vector/20221125/ourmid/pngtree-no-image-available-icon-flatvector-illustration-pic-design-profile-vector-png-image_40966566.jpg",
}
}
style={[styles.image, { width: 100, height: 100 }]}
/>
</View>
</TouchableOpacity>
</View>
)}
/>
<View style={styles.button}>
<Button
title="Previous"
onPress={() => {
setPage((prevPage) => prevPage - 1);
}}
disabled={page === 1}
/>
<Text>Page: {page}</Text>
<Button
title="Next"
onPress={() => {
setPage((prevPage) => prevPage + 1);
}}
disabled={page === totalPages}
/>
</View>
</View>
</View>
</View>
);
};
I've tried a bunch of things being changed to force the flatlist re-render, the data, keyExtractor and extraData props. I've tried useEffects, adding in state specifically designed to update after the data comes back. It just won't display and there's no error logged
It cannot be guaranteed that the totalItems you are updating in the handleSearch function will be updated before to the re-render. Because the setState function is asynchronous, it's possible that the state update won't be finished by the time you access totalItems in the useEffect that happens just after.
Use the setState callback function to make sure you are working with the latest available version of the state in order to fix issue.
You are using item.author in your FlatList keyExtractor, while item.volumeInfo.authors seems like the correct value. Set it to