1

My state is an array of objects representing books. Each book has a number of properties, including a "shelf" property, and an "id". The books are displayed according to the shelf property's setting: currentlyReading, wantToRead, or read.

Each book has a drop-down select that to allow the user to change the shelf setting. I have an event handler set up, which passes the new shelf and the book id.

With the setState call, I am trying to 1) isolate the particular book using the passed-in id property, then 2) change the shelf property of that book to the new one selected by the user, then 3) re-render with the new shelf, which should place the book in the new shelf. I am isolating the correct book by calling filter on the array, but after that I am getting lost. The code is below (JS first, then the JSX with the drop-down). How can I accomplish this, I tried several approaches, including passing in prevState. The latest one below is from an example I tried to emulate from reactTraining but it is giving me a Syntax error: Unexpected token in the setState call.

constructor() {
    super();
    this.state = {
        books: []
    }
};

componentDidMount() {
    getAll().then((books) => {
        this.setState({ books })
    })
}

onSelectChange = (e) => {
    const id = e.target.name
    const newShelf = e.target.value
    console.log(id, newShelf)
    let changeShelf = this.state.books.filter((book) => { return 
        book.id === id})

    this.setState({
        changeShelf[0].shelf: changeShelf[0].newShelf
    })
}

<li key={book.id}>
    <div className="book">
        <div className="book-top">
            <div className="book-cover" style={{ width: 128, height: 
                193, backgroundImage: `url("${ 
                book.imageLinks.thumbnail 
                }")` }}>
            </div>
            <div className="book-shelf-changer">
                <select name={book.id} onChange={onSelect}>
                    <option value="none" disabled>Move to...</option>
                    <option value="currentlyReading">Currently 
                        Reading</option>
                    <option value="wantToRead">Want to Read</option>
                    <option value="read">Read</option>
                    <option value="none">None</option>
                 </select>
            </div>
        </div>
        <div className="book-title">{book.title}</div>
        <div className="book-authors">{book.authors}</div>
    </div>
</li>

1 Answer 1

1

You can do it with a loop through the books. When you find the book with the id, just change the shelf property:

onSelectChange = (e) => {
    const id = e.target.name
    const newShelf = e.target.value
    console.log(id, newShelf)
    const books = this.state.books.slice(); // Create local copy to change.
    books.forEach((book) => {
      if (book.id === id) {
        book.shelf = newShelf;
      }
    });
    this.setState({ books });
}
Sign up to request clarification or add additional context in comments.

2 Comments

This worked, and I see why you need to create a local copy and change it, then swap it in. But I have one question - while I understand why the forEach loop works, why did you need to do the slice first? – HughB
the slice is used to make a local non-mutable copy of the state array.lorenstewart.me/2017/01/22/…

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.