Fork me on GitHub
#clojurescript
<
2022-09-15
>
Fredrik Holmqvist09:09:10

How do you trigger a Reagent element to rerender if the atom its state depends on has changed, but it doesn't rerender?

(let [completed (r/atom false)
  ...
  [:form {:on-submit (fn [e] (.preventDefault e) (reset! completed false))}
    ...
    [input {:type "checkbox" :value @completed}]])
If I pprint @completed I can see that completed toggles between true and false, but the checkbox remains checked.

p-himik09:09:46

IIRC, <input type="checkbox"> doesn't have a value but rather has a checked property.

Fredrik Holmqvist09:09:47

... that solved it. Cheers 😂

👍 1
Fredrik Holmqvist10:09:59

@U2FRKM4TW If you've got the time 🙂

Warning: Functions are not valid as a React child.
How do you map over a list of maps to generate components?
(defn todo-list []
  [:ul (map todo-item @todos)])
Tried various combinations of mapv, doall, doseq, for. Passing one map at a time works fine, todo-item works and todos is populated.

p-himik10:09:57

Are you sure that warning comes from that very component and not from some other place?

p-himik10:09:13

If yes, then I need full code and an example of what works.

Fredrik Holmqvist10:09:37

@U2FRKM4TW

(defn todo-item [{:keys [desc completed]}]
  (let [completed (r/atom completed)]
    (fn []
      [:li {:style {:color (if @completed "green" "red")}}
      [:p desc]
      [:button {:on-click #(reset! completed (not @completed))}
    "Toggle done"]])))

Fredrik Holmqvist10:09:44

I thought that the fn [] , but adding in a parameter changes nothing.

Fredrik Holmqvist10:09:54

What works is:

[todo-item (first @todos)]
What doesn't work is:
[:ul (map todo-item @todos)]

p-himik10:09:21

You're using todo-item as a function. But you gotta use it as a component. Here's how I'd use it:

(into [:ul]
      (map (fn [todo]
             [todo-item todo])
      @todos)
Also, unrelated but don't use reset! with @. Instead, use swap!: #(swap! completed not).

Fredrik Holmqvist10:09:54

Cheers!

(for [todo @todos] [todo-item todo)
Thanks for the swap! suggestion, much cleaner.

p-himik10:09:36

Note that your for code will produce a warning about missing :key. So if every todo item has a unique and stable ID of some kind, use it as a key. If not, you can use into to avoid that warning.

🙌 1
Fredrik Holmqvist13:09:31

Why does swap! turn the map into nil here? Calling without swap yields the correct output:

{1 {:flag false}, 2 {:flag true}}

p-himik13:09:49

Because the first argument to swap! is a function. But you're passing the result of (update-in ...) instead.

p-himik13:09:01

Write it as (swap! data update-in [1 :flag] not).

Fredrik Holmqvist13:09:29

Man, you're the hero of my day. Thank you, once again.

Fredrik Holmqvist13:09:19

@U2FRKM4TW I've got Calva up and running now, hopefully I'll be on my own two legs by now, cheers 😂

👍 1
Ben Lieberman19:09:13

if I use swap! like so (swap! winner calculate-winner state) where state is another r/atom must I deref state when I pass it as an arg?

p-himik19:09:52

In CLJS, it's not that important. But in CLJ, if you have concurrent code accessing those atoms, you'd have to rearrange your code in such a way so that there are no interdependent atoms.

👌 1