Fork me on GitHub
#re-frame
<
2021-06-11
>
zach00:06:53

When making forms in re-frame, what is the best way to have an initial value be based off a sub? For example, i have a shopping list with items having a default purchase quantity. e.g I buy eggs by the dozen. when i click on an item on the list, i want to put in how much of them i bought but have the default quantity value be the items' default purchase quantity.

zach00:06:01

right now, in my db i have a list of all items, and an "active-item" which is the id of the item i just clicked on. in my subs, then, i have item-to-buy which is the all the item details for the active-item id, taken from the list of all items. i am reading through the conduit real-world app, and they have a nice pattern for forms that's something like:

(def buy-item-form
  (let [quantity (reagent/atom 1)]
  (fn [item]
    [:form
      [input {:type "number" :value @quantity :on-change #(reset! quantity (-> % .-target .-value))}]
      ;; rest of the form...])))

zach00:06:48

I tried setting the initial atom to be based off the sub, eg:

(let [item (reframe-subscribe [::subs/item-to-buy])
      quantity (reagent/atom (:default-quantity @item)])
but find that the component often initializes before that subscription has a value, and it doesn't update when i actually call the function, and so the default value is always nil. I could assign the qty value to default within the fn call, but this gets called on every change to the form, which means the value is continually being reset to the default quantity. I feel like I'm missing something, but not sure what, and hoped this chat could help. Thank you!

zach00:06:14

in context, my ideal would be when someone clicks on eggs, the quantity value is already filled to 12, but they could change it to 6 or 18 or whatever exact amount they bought at that time.

afleck18:06:13

if the item map already has a default quantity assoc’d to it, you’re just going to do something like

(reg-sub ::subs/item-to-buy-default-quantity
  (fn [_qvec]
    (subscribe [::subs/item-to-buy]))
  (fn [item _qvec]
    (:default-quantity item)))

afleck18:06:26

also i would say that idiom is to deref in the binding, so (let [item @(subscribe [::subs/item-to-buy])] …) rather than deref when you use it.

afleck18:06:09

finally, your quantity can be in the db too! doesn’t need to be local state.

p-himik18:06:59

> that idiom is to deref in the binding Depends. It's not an idiom for form-2 or form-3 components.

afleck18:06:48

form? are you referring to docs’ “layer”?

p-himik18:06:40

I'm referring to Reagent view functions.

afleck18:06:37

ah i see. why is it not idiom there?

p-himik18:06:38

Because if you deref in the outer let then a form-2 or form-3 component will not be updated when the sub changes. There are cases when you want that behavior, but it's almost never the case.

afleck18:06:41

ahh that makes sense, thanks

afleck18:06:37

i guess i usually put my subscribes inside the render function

2
zach22:06:34

Ah, thank you for this! I think adding it to the db might be the simplest for how my app is structured. I'll give these a go!