0

I have booksOwned inside an array of objects, and I want to add another book to "John Doe"'s booksOwned.

const [ownerInfo, setOwnerInfo] = React.useState([
  {
    owner: "John Doe",
    booksOwned: ["To Kill a Mockingbird", "1984"],
  },
  {
    owner: "John Doe Jr",
    booksOwned: [
      "Harry Potter and the Philosopher's Stone",
      "The Lord of the Rings",
    ],
  },
])

This is what I've tried so far, does not seem to work.

let data = ownerInfo
let newBook = "Pride and Prejudice"

//to store currently owned books
let currBooks = []

//get the index of object I need
let ind = data.map((val, i) => {
  if (val.owner === "John Doe") {
    currBooks = val.booksOwned
    return i
  } else {
    return -1
  }
})

let bookIndex = currBooks.indexOf(newBook)

//insert only if book does not yet exist
if (bookIndex === -1) {
  data[ind] = {
    owner: "John Doe",
    booksOwned: currBooks.push(newBook),
  }
  setOwnerInfo(data)
}
2
  • you should use let data = [...ownerInfo] instead of let data = ownerInfo. Commented Jul 10, 2020 at 9:35
  • Seems a little convoluted, but what do you mean by “doesn’t work”? You’re directly modifying state by pushing into the books array. Commented Jul 10, 2020 at 9:37

2 Answers 2

2

A simple approach would be to use functional setState. Basically, setState can accept function as it's argument. Something like this:

const newBook = 'Pride and Prejudice'

// When using setOwnerInfo with functions as argument, it
// receives currentState as argument so we can use it.

// Use map to create a new array, instead of modifying it.
setOwnerInfo(owners => owners.map(owner => {
  // First check for correct owner. Return if this is wrong one.
  if (owner.onwer !== "John Doe") { return owner }

  // Second, check that book doesn't exist already. Return if it does.
  if (owner.booksOwned.find(ownedBook => ownedBook === newBook) !== undefined) { return owner }

  // Book doesn't exist. Add it.
  // Let's create a new object, instead of mutating it.
  return {...owner, booksOwned: [...owner.booksOwner, newBook]}
}))

One thing about react state is that you shouldn't mutate it. It can lead to many bugs. It's better to just copy it and save yourself a hassle in the future.

In case you want to filter a book away, you can just change it like this:

setOwnerInfo(owners => owners.map(owner => {
  // First check for correct owner. Return if this is wrong one.
  if (owner.onwer !== "John Doe") { return owner }

  // Filter here
  // Let's create a new object, instead of mutating it.
  return {...owner, booksOwned: owner.booksOwned.filter(ownedBook => ownedBook !== newBook)}
}))

This should remove all array items that match newBook (even if somehow there are duplicates, which shouldn't happen since we're guarding against duplicates above).

Sign up to request clarification or add additional context in comments.

2 Comments

Wow, this works, but what should I do if I want to remove a book from booksOwned?
I've edited my answer to contain filtering option as well
1

Here is an easy way to do this in ES6 Javascript:

// owner set to name of the owner
// newBook set to book title
const addBook = (owner, newBook) => {
  // Find owner by name
  const person = ownerInfo.find(person => person.owner === owner)
  // if book already owned escape function
  person.booksOwned.find(exists => { if(exists == newBook) return })
  // Add book to owned books
  person.booksOwned.push(newBook)
}

// example on how to use
addBook("John Doe", "New Book")

Or if you want to use it with setState:

const addBook = (owner, newBook) => {
  const person = ownerInfo.find(person => person.owner === owner)
  const newOwnerInfo = person.booksOwned.push(newBook)
  person.booksOwned.find(exists => { if(exists == newBook) return })
  setOwnerInfo(newOwnerInfo) // to change state
}

This function can be used for any of the owners.

Hope this helps.

Comments

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.