I am attempting to do a book search with the bookTitle
that users type in or select from the page. On first render bookTitle
comes back undefined, which then pulls up some random kids book called 'undefined'. By putting the bookTitle
in the dependency array of my useEffect
, I expected this issue to be resolved, but now this causes it to flash the undefined book, then the desired book, and then back to undefined.
const location = useLocation()
const { data } = location.state
const [apiData, setApiData] = useState<GoogleBooks>()
const [bookTitle, setBookTitle] = useState()
const squishedTitle = data.content.title.replace(/ /g, '').toLowerCase()
useEffect(() => {
setBookTitle(squishedTitle)
fetch(`https://www.googleapis.com/books/v1/volumes?q=${bookTitle}&key=${import.meta.env.VITE_GOOGLE_API_KEY}`)
.then(res => res.json())
.then(response => setApiData(response))
.catch((err) => console.error(err))
}, [bookTitle])
I am expecting to never receive undefined when a user clicks on a book.
The code has defined bookTitle
to be initially undefined.
const [bookTitle, setBookTitle] = useState(); // <-- undefined
This means that for the initial render cycle that bookTitle
is undefined, and then at the end of the render cycle the effect runs and enqueues a state update to update bookTitle
to the value of squishedTitle
and that bookTitle
is undefined for the fetch request.
Just initialize the bookTitle
state to the computed value directly so it's available on the initial render cycle.
const { state } = useLocation();
const [apiData, setApiData] = useState<GoogleBooks>();
const [bookTitle, setBookTitle] = useState(
state?.data.content.title.replace(/ /g, '').toLowerCase() || ""
);
useEffect(() => {
fetch(`https://www.googleapis.com/books/v1/volumes?q=${bookTitle}&key=${import.meta.env.VITE_GOOGLE_API_KEY}`)
.then(res => res.json())
.then(response => setApiData(response))
.catch((err) => console.error(err))
}, [bookTitle]);
If you are not modifying the bookTitle
state elsewhere then it may be preferable to use the data
value directly as a dependency and forego the local state.
Example:
const { state } = useLocation();
const [apiData, setApiData] = useState<GoogleBooks>();
const bookTitle = state?.data.content.title.replace(/ /g, '').toLowerCase() || "";
useEffect(() => {
fetch(`https://www.googleapis.com/books/v1/volumes?q=${bookTitle}&key=${import.meta.env.VITE_GOOGLE_API_KEY}`)
.then(res => res.json())
.then(response => setApiData(response))
.catch((err) => console.error(err))
}, [bookTitle]);