You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.03 01:40:55