Reputation: 6088
I've got a Reagent atom:
(defonce order (r/atom {:firstName "" :lastName "" :toppings [] }))
I want to add toppings to the :toppings
vector. I have tried many variations on:
(swap! (:toppings order) conj "Pepperoni")
which gives me: Uncaught Error: No protocol method ISwap.-swap! defined for type null:
(swap! order :toppings "Pepperoni")
sort of works, but just updates order, not the :toppings
vector. When I deref order
, I just get the latest value.
What is the right way to add (and remove) values to my :toppings
vector?
Upvotes: 2
Views: 1066
Reputation: 1225
Just to explain a little more, when you do (swap! (:toppings order) ...)
, you are retrieving the :toppings
key from order
, which would make sense if it were a map, but it's an atom, so (:toppings order)
returns nil
.
The first argument to swap!
should always be an atom (Reagent
atoms work the same way). The second argument should be a function which takes the content of the atom as its first argument. Then, you can optionally provide more arguments that will be passed to the function argument.
Instead of minhtuannguyen's answer, you could do the following:
(swap! order
(fn a [m]
(update m :toppings
(fn b [t]
(conj t "Pepperoni")))))
fn a
receives the map inside the atom, binds it to m
, then updates it and returns a new map, which becomes the new value of the atom.
If you wanted to, you could redefine fn a
to take a second argument:
(swap! order
(fn a [m the-key]
(update m the-key
(fn b [t]
(conj t "Pepperoni"))))
:toppings)
:toppings
is now being passed as the second argument to fn a
, and then passed to update
inside of fn a
. We could do the same to the third argument to update
:
(swap! order
(fn a [m the-key the-fn]
(update m the-key the-fn))
:toppings
(fn b [t]
(conj t "Pepperoni")))
Now update
has the same signature as fn a
, so we no longer need fn a
at all. We can simply provide update
directly in place of fn a
:
(swap! order update :toppings
(fn b [t]
(conj t "Pepperoni")))
But we can keep going, because update
also accepts more arguments which it then passes to the function provided to it. We could rewrite fn b
to take another argument:
(swap! order update :toppings
(fn b [t the-topping]
(conj t the-topping))
"Pepperoni"))
Once again, conj
has the same signature as fn b
, so fn b
is redundant, and we can just use conj
in its place:
(swap! order update :toppings conj "Pepperoni")
Thus, we end up with minhtuannguyen's answer.
Upvotes: 6
Reputation: 10514
I would turn toppings
into a set. I don't think you want duplicate toppings in the collection, so a set is appropriate:
(defonce order (r/atom {:first-name "" :last-name "" :toppings #{}})) ; #{} instead of []
Then you can still conj
as stated in the other answer:
(swap! order update :toppings conj "Pepperoni")
but you can also disj
:
(swap! order update :toppings disj "Pepperoni")
Upvotes: 5
Reputation: 1054
You can update toppings with:
(swap! order update :toppings conj "Pepperoni")
Upvotes: 4