Hi I am creating an app where a user can search for a book and put it on a shelf depending on which shelf the user clicks on. Currently the user can type a query and many results can get displayed. The user can open a dropdown on a book and click on a shelf (in the dropdown) to select a shelf for that book.
I want to call a method that will update the shelf of a book. It works only if the shelfType is hardcoded however (shelfTypes are ‘wantToRead’, ‘read’, ‘currentlyReading’). What I want to happen is that the user clicks on a shelf and that shelf is set as the local state variable shelfType in SearchPage. Then once the shelfType changes, the method to update the shelf of a book will run (it makes an API call to a backend).
But for some strange reason I can only update the shelf if I hardcode the shelf type into the update method, not when I use the value of the state shelfType. What am I doing wrong? I hope this question makes sense.
SearchPage.js
import React, { useEffect, useState } from 'react'; import { BsArrowLeftShort } from 'react-icons/bs'; import SearchBar from '../components/SearchBar'; import { search, update, getAll } from '../api/BooksAPI'; import Book from '../components/Book'; const SearchPage = () => { const [query, setQuery] = useState(''); const [data, setData] = useState([]); const handleChange = (e) => { setQuery(e.target.value); }; useEffect(() => { const bookSearch = setTimeout(() => { if (query.length > 0) { search(query).then((res) => { if (res.length > 0) { setData(res); } else setData([]); }); } else { setData([]); // make sure data is not undefined } }, 1000); return () => clearTimeout(bookSearch); }, [query]); const [shelfType, setShelfType] = useState('None'); const [currentBook, setCurrentBook] = useState({}); const doSomethingWithBookAndShelf = (book, shelf) => { setShelfType(shelf); setCurrentBook(book); }; useEffect(() => { //following line doesn't update like this, but I want it to work like this update(currentBook, shelfType).then((res) => console.log(res)); // update works if I run update(currentBook, 'wantToRead').then((res) => console.log(res)); getAll().then((res) => console.log(res)); }, [shelfType]); return ( <div> <SearchBar type="text" searchValue={query} placeholder="Search for a book" icon={<BsArrowLeftShort />} handleChange={handleChange} /> <div className="book-list"> {data !== [] ? data.map((book) => ( <Book book={book} key={book.id} doSomethingWithBookAndShelf={doSomethingWithBookAndShelf} /> )) : 'ok'} </div> </div> ); }; export default SearchPage;
Book.js
import React from 'react'; import PropTypes from 'prop-types'; import ButtonDropDown from './ButtonDropDown'; const Book = ({ book, doSomethingWithBookAndShelf }) => { return ( <div className="book"> <img src={book.imageLinks.thumbnail} alt={book.title} className="book-thumbnail" /> <ButtonDropDown choices={['Currently Reading', 'Want to Read', 'Read', 'None']} onSelectChoice={(choice) => { // book came from the component props doSomethingWithBookAndShelf(book, choice); }} /> <div className="book-title">{book.title}</div> <div className="book-authors">{book.authors}</div> </div> ); }; Book.propTypes = { doSomethingWithBookAndShelf: PropTypes.func.isRequired, book: PropTypes.shape({ imageLinks: PropTypes.shape({ thumbnail: PropTypes.string.isRequired, }), title: PropTypes.string.isRequired, authors: PropTypes.arrayOf(PropTypes.string), }).isRequired, }; export default Book;
ButtonDropDown.js
import React, { useState } from 'react'; import PropTypes from 'prop-types'; import { BsFillCaretDownFill } from 'react-icons/bs'; const ButtonDropDown = ({ choices, label, onSelectChoice }) => { const [active, setActive] = useState(false); const toggleClass = () => { setActive(!active); }; return ( <div className="dropdown"> <button type="button" className="dropbtn" onFocus={toggleClass} onBlur={toggleClass} > <BsFillCaretDownFill /> </button> <div id="myDropdown" className={`dropdown-content ${active ? `show` : `hide`}`} > <div className="dropdown-label">{label}</div> {choices.map((choice, index) => ( <button // eslint-disable-next-line react/no-array-index-key key={index} className="dropdown-choice" onClick={() => { // we create an specific callback for each item onSelectChoice(choice); }} type="button" value={choice} > {choice} </button> ))} </div> </div> ); }; ButtonDropDown.propTypes = { choices: PropTypes.arrayOf(PropTypes.string).isRequired, label: PropTypes.string, onSelectChoice: PropTypes.func.isRequired, }; ButtonDropDown.defaultProps = { label: 'Move to...', }; export default ButtonDropDown;
Advertisement
Answer
Cause you’re “Want to Read” text in choices is different
choices={['Currently Reading', *'Want to Read'*, 'Read', 'None']}
Based on this // update works if I run update(currentBook, 'wantToRead').then((res) => console.log(res));
“wanToRead” is not equal to “Want to Read”