Home > OS >  "setState hook is not a function" error in one function, but not another one
"setState hook is not a function" error in one function, but not another one

Time:09-16

I'm making a little project with the google books API and I have two handlers, removeBook and addToShelf, that do essentially the same thing: sends a request to my server, which makes a call to google books and returns an updated list, then updates the component state with the new list. The code is nearly identical, but removeBook works and addToShelf doesn't.

When a book is added, the setState functions return TypeError: setHaveReadList is not a function (or setReadingNowList or setToReadList, whichever method is being called in the switch statement.

Similar questions on SO talk about importing useState and making sure that the initial state is the right data type. I know that's not the problem, and if there were more general issues like that then removeBook wouldn't work, but it does.

Here's MyLibrary, where useState is called
import React, { useEffect, useState } from "react";
// other imports

const MyLibrary = () => {
  const [toReadList, setToReadList] = useState(null);
  const [readingNowList, setReadingNowList] = useState([]);
  const [haveReadList, setHaveReadList] = useState([]);

  useEffect(() => {
    //initial lists are set here, this all works fine
  }, []);

  return (
      <Container maxWidth="95%">
        <Tabs
          onChange={(index) => {
            setActiveTab(index);
          }}
        >
          <TabList>
            <Tab>To Read</Tab>
            <Tab>Reading Now</Tab>
            <Tab>Have Read</Tab>
          </TabList>
          {!toReadList ? (
            <Container centerContent>
              <Spinner mt={10} size="xl" />
            </Container>
          ) : (
            <TabPanels>
              <TabPanel textAlign="center">
                {toReadList.length === 0 ? (
                  <EmptyShelf />
                ) : (
                  <CardGridShelf
                    books={toReadList}
                    shelfId={2}
                    setToReadList={setToReadList}
                  />
                )}
              </TabPanel>
              <TabPanel w="100%" h="auto" textAlign="center">
                {readingNowList.length === 0 ? (
                  <EmptyShelf />
                ) : (
                  <CardGridShelf
                    books={readingNowList}
                    shelfId={3}
                    setReadingNowList={setReadingNowList}
                  />
                )}
              </TabPanel>
              <TabPanel textAlign="center">
                {haveReadList.length === 0 ? (
                  <EmptyShelf />
                ) : (
                  <CardGridShelf
                    books={haveReadList}
                    shelfId={4}
                    setHaveReadList={setHaveReadList}
                  />
                )}
              </TabPanel>
            </TabPanels>
          )}
        </Tabs>
      </Container>
  );
};

export default MyLibrary;

BookDetailShelf, where removeBook and addToShelf are:
import React from "react";
//other imports

const BookDetailShelf = ({ <-- I know these props are passed correctly
  isOpen,
  onClose,
  bookData,
  shelfId,
  setToReadList,
  setReadingNowList,
  setHaveReadList,
}) => {
  const token = useToken().token;
  const toast = useToast();

  const removeBook = (bookId, shelfId, token) => {
    axios
      .get("http://localhost:5000/remove-book", {
        params: {
          shelfId: shelfId,
          token: token,
          bookId: bookId,
        },
      })
      .then((response) => {
        let updatedBookList = response.data.bookResponse; //<--returns an array
        if (updatedBookList === undefined) {
          updatedBookList = [];
        }
        switch (response.data.shelfResponse) {
          case "2":
            setToReadList(updatedBookList);
            break;
          case "3":
            setReadingNowList(updatedBookList);
            break;
          case "4":
            setHaveReadList(updatedBookList);
            break;
        }
        toast({
          title: "Book removed!",
          status: "success",
          duration: 5000,
          isClosable: true,
        });
        onClose();
      })
  };

  const addToShelf = (bookId, shelfId, token) => {
    axios
      .get("http://localhost:5000/add-to-shelf", {
        params: {
          bookId: bookId,
          shelfId: shelfId,
          token: token,
        },
      })
      .then((response) => {
        let updatedBookList = response.data.bookResponse; //<--returns an array
        if (updatedBookList === undefined) {
          updatedBookList = [];
        };
        switch (response.data.shelfResponse) {
          case "2":
            setToReadList(updatedBookList); //<-- these throw the error
            break;
          case "3":
            setReadingNowList(updatedBookList); //<-- these throw the error
            break;
          case "4":
            setHaveReadList(updatedBookList); //<-- these throw the error
            break;
        }
        toast({
          title: "Book added!",
          status: "success",
          duration: 5000,
          isClosable: true,
        });
        onClose();
      })
  };

CodePudding user response:

I think the problem is in setToReadList and the others. It is not a best practies to pass a setState from Parent to Child directly, you must wrap inside a function in the parent and then pass the function to the child.

//Original setState in the parent

const [state,setState]= useState([])

//function that wraps the setstate and can be passed down to child.

handleSetState()=>setState({...})

Also I was wondering..axios.get? Maybe axios.put or post when you send an object to server?

Hope for the best,

Mauro

  • Related