2017-09-27 10 views
1

J'ai un atome réactif:Comment mettre à jour vecteur réactif dans un atome

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

Je veux ajouter des garnitures au vecteur :toppings. J'ai essayé beaucoup de variations sur:

(swap! (:toppings order) conj "Pepperoni") qui me donne: Uncaught Error: No protocol method ISwap.-swap! defined for type null:

(swap! order :toppings "Pepperoni") genre de travaux, mais juste les mises à jour l'ordre, pas le vecteur :toppings. Quand je deref order, je viens d'obtenir la dernière valeur.

Quelle est la bonne façon d'ajouter (et supprimer) des valeurs à mon vecteur :toppings?

+0

Toutes les réponses ci-dessous sont utiles. J'ai accepté la réponse qui répond le plus directement à la question, mais les autres offrent un meilleur aperçu de la façon de travailler avec les atomes. – jmargolisvt

Répondre

3

Vous pouvez mettre à jour les garnitures avec:

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

Je tournerais toppings en un ensemble. Je ne pense pas que vous voulez dupliquer des garnitures dans la collection, donc un ensemble est approprié:

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

Ensuite, vous pouvez toujours conj comme indiqué dans l'autre réponse:

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

mais vous pouvez aussi disj :

(swap! order update :toppings disj "Pepperoni") 
+0

Et si quelqu'un veut doubler la quantité de pepperoni? ;-) –

+0

@NathanDavis Assez juste. :) Utilisez une table de hachage de la tête de nom à la quantité. Par exemple. '{" pepperoni "2}'. Pour augmenter '(update-dans l'ordre [: toppings" pepperoni "] inc)' ou 'dec' pour diminuer. Peut-être supprimer une entrée lorsque la valeur est 0. –

3

Juste pour expliquer un peu plus, quand vous faites (swap! (:toppings order) ...), vous récupérez la clé de :toppingsorder, ce qui aurait du sens si c'était une carte, mais c'est un atome, alors (:toppings order) renvoie nil.

Le premier argument à swap! devrait toujours être un atome (Reagent les atomes fonctionnent de la même manière). Le deuxième argument devrait être une fonction qui prend le contenu de l'atome comme premier argument. Ensuite, vous pouvez éventuellement fournir plus d'arguments qui seront passés à l'argument de la fonction.

au lieu de la réponse de minhtuannguyen, vous pourriez faire ce qui suit:

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

fn a reçoit la carte dans l'atome, se fixe à m, puis met à jour et retourne une nouvelle carte, qui devient la nouvelle valeur de l'atome.

Si vous voulez, vous pouvez redéfinir fn a de prendre un second argument:

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

:toppings est maintenant passé comme second argument à fn a, puis passé à l'intérieur de updatefn a. Nous pourrions faire la même chose pour le troisième argument update:

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

Maintenant update a la même signature que fn a, nous avons donc plus besoin fn a du tout.Nous pouvons simplement fournir update directement à la place de fn a:

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

Mais nous pouvons continuer, parce que update accepte aussi plus d'arguments qu'il transmet ensuite à la fonction qui lui est fournie. Nous pourrions réécrire fn b prendre un autre argument:

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

Encore une fois, conj a la même signature que fn b, donc fn b est redondant, et nous pouvons simplement utiliser conj à sa place:

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

Ainsi , on se retrouve avec la réponse de minhtuannguyen.