Data requested from google books api will display on react-native webview, but will not in emulator/phone

44 Views Asked by At

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

1

There are 1 best solutions below

0
Pratik Prakash Bindage On

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.

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();
    
    setTotalItems(data.totalItems, () => {
      setSearchResults(data.items);
      if (data.totalItems <= page * 20 + 20) {
        setHasMore(false);
      }
    });
  } catch (error) {
  }
};

You are using item.author in your FlatList keyExtractor, while item.volumeInfo.authors seems like the correct value. Set it to

keyExtractor={(item) =>
  `${item.volumeInfo.title}-${item.volumeInfo.authors}-${Math.random()}`
}