Major Headache in Quotes App


Uncategorized

Updated Aug 6th, 2021

So I have an “admin” page which renders all of the quotes in the database. When clicking an edit button, this opens up a modal and a backdrop and you type in any updates you want and hit a “save updates” button.

The major headache was that, although I could get it to update in the database, I couldn’t get the updated quotes to show on the page.

When a user logs in and is routed to the admin page, an http request fires off fetching all the quotes from the database and store these quotes in global state.

So it was the updating of this global state that was giving me the headache.

I was getting an error about the “component being unmounted” and/or “map not being defined” and I just culdn’t figure it out. I hit this roadblock and was stuck here for hours.

The Solution

Well first let me say I wasn’t storing the quotes in global state using the “useContext” hook. I was passing down props two components deep from the admin page to a quote item to the edit model and this was adding to the confusion.

So the implementation of the global state was a start. Then I was able to update this global state by just having the updated item and removing everything else. It was the mapping over the existing items that was giving me the problem. I needed to look at each quote in global state, find the quote “object” with the matching id, update the quote and source “keys” with the newly edited values, and return all other quotes the same way they were.

One solution I thought of was to use the “findIndex” method in combo with the “.slice(). method. I’m glad I didn’t go down this route. Although I did learn that “slice” does not mutate the data.

I created a separate test file and successfully did what I wanted to do. I just couldn’t recreate it in the actual app.

import { useState } from "react"

function Testing(props) {
  const [products, setProducts] = useState([
    { id: 1, item: "ipad", price: 400 },
    { id: 2, item: "iphone8", price: 900 },
    { id: 3, item: "ipad2", price: 225 },
    { id: 4, item: "iphoneX", price: 1200 },
    { id: 5, item: "appleTV", price: 600 },
    { id: 6, item: "apple Watch", price: 425 }
  ])

  function testHandler() {
    let editedItem = {
      id: 3,
      item: "ipad_two",
      price: 199
    }
    console.log(editedItem)
    console.log(products)

    setProducts(products.map(item => (item.id === editedItem.id ? { ...item, item: "ipad_two" } : item)))

    console.log(products)
  }

  return (
    <>
      <h1>Test Page</h1>
      <button onClick={testHandler}>Edit Test</button>
    </>
  )
}

export default Testing

Until I tried this.

/* The code below works but moves the edited item to the top */
/* It aslo doesn't include all of the necessary keys in the edited object*/
          
const filteredQuotes = quotesCtx.loadedQuotes.filter(item => {
  return item._id !== props.id
})

quotesCtx.setLoadedQuotes([
  {
  _id: state.id,
  quote: state.quote.value,
  source: state.source.value
  },
  ...filteredQuotes
])

This was the final implementation that kept the edited item in its same place (Not moving it to the top or bottom of the list) and had all of the same keys that were not edited (“createdDate” and “author”). It was a long winding road, but I got there.

const filteredQuotes = quotesCtx.loadedQuotes.map(item => {
  if (item._id == props.id) {
    return { ...item, quote: state.quote.value, source: state.source.value }
  } else {
    return item
  }
})

quotesCtx.setLoadedQuotes([...filteredQuotes])

Looking back I learned the importance of global state using the “useContext” hook. The other big thing, and I’ve seen this before, is the type of data you working with. I believe this is what was causing the map error. The initial state for the “loadedQuotes” was empty.

const [loadedQuotes, setLoadedQuotes] = useState()

So when I was attempting to add the quote objects to the global state, I wasn’t “spreading” them in so I wasn’t working with an array of objects like I thought I was and this was causing the “map” error.

In addition, the request wasn’t in a “useEffect” with a teardown function so I was getting an “unmounted” error.

I learned a lot working through this: