Как обновить вектор реагента в атоме

У меня есть атом Реагента:

(defonce order (r/atom {:firstName "" :lastName "" :toppings [] }))

Я хочу добавить начинки к :toppings вектор. Я перепробовал много вариантов:

(swap! (:toppings order) conj "Pepperoni") что дает мне: Uncaught Error: No protocol method ISwap.-swap! defined for type null:

(swap! order :toppings "Pepperoni") вроде работает, но просто обновляет порядок, а не :toppings вектор. Когда я разыграю orderЯ только что получил последнее значение.

Как правильно добавить (и удалить) значения в мой :toppings вектор?

3 ответа

Решение

Вы можете обновить начинки с:

(swap! order update :toppings conj "Pepperoni")

Просто чтобы объяснить немного больше, когда вы делаете (swap! (:toppings order) ...), вы получаете :toppings ключ от orderчто бы иметь смысл, если бы это была карта, но это атом, так (:toppings order) возвращается nil,

Первый аргумент swap! всегда должен быть атом (Reagent атомы работают так же). Второй аргумент должен быть функцией, которая принимает содержимое атома в качестве первого аргумента. Затем вы можете указать дополнительные аргументы, которые будут переданы в аргумент функции.

Вместо ответа minhtuannguyen вы можете сделать следующее:

(swap! order
  (fn a [m]
    (update m :toppings
      (fn b [t]
        (conj t "Pepperoni")))))

fn a получает карту внутри атома, привязывает ее к m, затем обновляет его и возвращает новую карту, которая становится новым значением атома.

Если бы вы хотели, вы могли бы переопределить fn a принять второй аргумент:

(swap! order
  (fn a [m the-key]
    (update m the-key
      (fn b [t]
        (conj t "Pepperoni"))))
  :toppings)

:toppings в настоящее время передается в качестве второго аргумента fn a, а затем перешел к update Внутри fn a, Мы могли бы сделать то же самое с третьим аргументом update:

(swap! order
  (fn a [m the-key the-fn]
    (update m the-key the-fn))
  :toppings
  (fn b [t]
    (conj t "Pepperoni")))

Сейчас update имеет ту же подпись, что и fn aтак что нам больше не нужно fn a совсем. Мы можем просто предоставить update прямо на месте fn a:

(swap! order update :toppings
  (fn b [t]
    (conj t "Pepperoni")))

Но мы можем продолжать, потому что update также принимает больше аргументов, которые затем передает переданной ему функции. Мы могли бы переписать fn b принять другой аргумент:

(swap! order update :toppings
  (fn b [t the-topping]
    (conj t the-topping))
  "Pepperoni"))

Снова, conj имеет ту же подпись, что и fn b, так fn b избыточно, и мы можем просто использовать conj на его месте:

(swap! order update :toppings conj "Pepperoni")

Таким образом, мы в конечном итоге с ответом minhtuannguyen.

Я бы включил toppings в набор. Я не думаю, что вы хотите дубликаты в коллекции, поэтому набор подходит:

(defonce order (r/atom {:first-name "" :last-name "" :toppings #{}})) ; #{} instead of []

Тогда вы можете еще conj как указано в другом ответе:

(swap! order update :toppings conj "Pepperoni")

но вы также можете disj:

(swap! order update :toppings disj "Pepperoni")
Другие вопросы по тегам