2

I have a state subscriptions that is an array of objects. When I get a websocket message I grab symbol and price. With these I want to setSubscriptions and either edit the matching object if symbol === name or if it doesn't exist, add it. I figured out how edit price if it exists, but how can I add a new entry to the array if it doesn't?

const [subscriptions, setSubscriptions] = useState([{name: 'xbtusd', price: ''}])


const updateSubscription = (symbol, price) => {
    setSubscriptions((prevSubscriptions) =>
      prevSubscriptions.map((instrument) =>
        instrument.name === symbol
          ? { ...instrument, price: price }
          : instrument
      )
    )  
}
user4584963
  • 2,403
  • 7
  • 30
  • 62

4 Answers4

1

prevSubscriptions.push(newElement) will do what you are looking for

You might consider changing your data structure -- why are you using an array? Perhaps an object (map) with the symbol as the key would be easier:

setPrices({[symbol]: price });
Codebling
  • 10,764
  • 2
  • 38
  • 66
  • I was reading its not a good idea to use nested state in react like [this answer](https://stackoverflow.com/a/51136076/4584963) suggests – user4584963 Feb 10 '21 at 02:17
  • @user4584963 wow ok I actually didn't know that - I don't usually work with state like that. It *does* say that you should flatten them -- in this case if you just used a price state object then this would work. Updating answer to reflect that – Codebling Feb 10 '21 at 03:00
  • You're either fighting React or fighting the array - you have to decide which fight is easier to win :) Maybe splitting up your data is easier, but maybe not. Also I've not tested but some of the answers at that link suggest that if you're creating a new object (as I was), the shallow compare would realize the objects are not equal and trigger an update, but you might be playing with fire – Codebling Feb 10 '21 at 03:04
1
const updateSubscription = (symbol, price) => {
    setSubscriptions((prevSubscriptions) => {      
      const next = prevSubscriptions.map((instrument) =>
        instrument.name === symbol
          ? { ...instrument, price: price }
          : instrument
      )

      // look for it, if not there add
      if (!prevSubscriptions.find(x => x.name === symbol)) {
        return [...next, putNewObjectHere]
      } else return next
    )

}
azium
  • 20,056
  • 7
  • 57
  • 79
1

I would do something like this to add to your existing code:

const updateSubscription = (symbol, price) => {
   const if_exists = subscriptions.find(i => symbol === i.name)
    setSubscriptions((prevSubscriptions) => {
      return if_exists ? 

        prevSubscriptions.map((instrument) =>
          instrument.name === symbol
            ? { ...instrument, price: price }
            : instrument
        ) :

        [...prevSubscriptions, {name: symbol, price}]
      
      }
    )  
}
codemonkey
  • 7,325
  • 5
  • 22
  • 36
1

you could keep to one loop only if you create a found variable for control, removing unnecessary loops. You would only change its value inside map loop if there's a match:

const updateSubscription = (symbol, price) => {
  setSubscriptions((prevSubscriptions) => {
    let found = false

    const subscriptions = prevSubscriptions.map((instrument) => {
      if (instrument.name !== symbol) return instrument
      found = true
      return { ...instrument, price: price }
    })
    
    if(found) return subscriptions

    subscriptions.push({ name: symbol, price })
    return subscriptions
  })
}
buzatto
  • 9,704
  • 5
  • 24
  • 33