Search code examples
clojurescript

How to update an element in vector in atom state


I'm trying to create kind of todo list with ClojureScript and reagent framework. I defined app state as atom:

(def app-state
  (r/atom
   {:count 3
    :todolist
    [{:id 0 :text "Start learning mindcontrol" :finished true}
     {:id 1 :text "Read a book 'Debugging JS in IE11 without pain'" :finished false}
     {:id 2 :text "Become invisible for a while" :finished false}]}))

Have a function to update todo list:

(defn update-todolist [f & args]
  (apply swap! app-state update-in [:todolist] f args))

And function toggle todo:

(defn toggle-todo [todo]
  (update-todolist update-in [2] assoc :finished true))

Here I'm updating vector element directly by its index right now.

I'm rendering every item with this function:

(defn item [todo]
  ^{:key (:id todo)}
  [:div
   [:span {:class "item-text"} (:text todo)]
   [:i {:class (str "ti-check " (if (:finished todo) "checked" "unchecked"))
        :on-click #(toggle-todo (assoc todo :finished true))}]])

Here I'm passing updated todo but it's not correct to pass always true. Probably it would be enough to pass its index and it will solve my problem, but I have no idea how to do this.


Solution

  • (def app-state
      (r/atom
       {:count 3
        :todolist
        [{:id 0 :text "Start learning mindcontrol" :finished true}
         {:id 1 :text "Read a book 'Debugging JS in IE11 without pain'" :finished false}
         {:id 2 :text "Become invisible for a while" :finished false}]}))
    
    
    (defn update-todolist [f & args]
      (apply swap! app-state update-in [:todolist] f args))
    
    
    (defn toggle-todo [todo]
      (swap! app-state update-in [:todolist (:id todo) :finished] not))
    
    
    (defn item [todo]
      ^{:key (:id todo)}
      [:div
       [:span {:class "item-text"} (:text todo)]
       [:i {:class (str "ti-check " (if (:finished todo) "checked" "unchecked"))
            :on-click #(toggle-todo todo)}]])