React Native中Google Books API数据在真机/模拟器不显示问题
问题分析与修复方案
你的代码存在几个关键问题,导致FlatList在真机/模拟器上无法更新显示,以下是具体问题和修复步骤:
1. 重复的useEffect监听page
代码里出现了两次完全相同的useEffect监听page变化:
useEffect(() => { handleSearch(); }, [page]);
这会导致page变动时重复触发搜索请求,引发状态更新冲突,直接删除其中一个即可。
2. startIndex计算错误
Google Books API的startIndex从0开始计数,但你的代码用page * 20,第一页的startIndex会变成20,直接跳过前20条数据,导致请求结果异常。正确计算应为:
startIndex={(page - 1) * 20}
3. keyExtractor逻辑错误
当前keyExtractor使用了不存在的item.author字段,还加入Math.random()导致每次渲染生成新key,破坏FlatList的组件复用机制,甚至引发渲染异常。应该用书籍的唯一ID:
keyExtractor={(item) => item.id}
4. extraData未包含必要依赖
extraData需要包含所有影响FlatList渲染的状态,当前仅传totalItems无法感知分页或搜索关键词的变化,应改为:
extraData={[page, searchQuery]}
5. 未处理API返回空结果的情况
当搜索无结果时,data.items可能为undefined,直接赋值给searchResults会导致FlatList的data无效,需默认设为空数组:
setSearchResults(data.items || []);
6. 冗余的handlePageChange函数
handlePageChange定义后未被使用,且内部重复调用handleSearch会和useEffect的触发逻辑冲突,直接删除该函数即可。
修改后的完整代码
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 - 1) * 20 }&maxResults=20&key=${api}` ); const data = await response.json(); console.log(data); setTotalItems(data.totalItems || 0); setSearchResults(data.items || []); // 修正分页判断逻辑 setHasMore(page * 20 < data.totalItems); } catch (error) { console.error(error); } }; useEffect(() => { handleSearch(); }, [page, searchQuery]); // 加入searchQuery,确保关键词变化时触发搜索 const handleSelectBook = (book) => { setSelectedBook(book); setTitle(book.volumeInfo.title); setAuthors(book.volumeInfo.authors?.join(", ") || ""); setDescription(book.volumeInfo.description || ""); setImgUrl( 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, navigation]); // 加入navigation依赖 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.id} extraData={[page, searchQuery]} 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?.join(", ") || "Unknown"} </Text> <Image source={{ uri: item.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" }} 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={!hasMore || page >= totalPages} /> </View> </View> </View> </View> ); };
额外优化说明:
- 给
totalItems设置默认值0,避免初始计算totalPages时出现NaN - 优化
hasMore的判断逻辑,更准确判断是否存在下一页 - 给
handleSelectBook中的字段添加默认值,避免出现undefined - 导航的
useEffect加入navigation依赖,符合React Hooks规则
内容的提问来源于stack exchange,提问作者Jake
相关产品推荐
相关产品推荐

