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