re-frame

https://github.com/Day8/re-frame/blob/master/docs/README.md https://github.com/Day8/re-frame/blob/master/docs/External-Resources.md
zackteo 2021-06-20T01:34:45.143300Z

Hello, what should I be using/doing to get the state in the re-frame db and edit it. Like how to I get a copy of the data I want in the db so I can manipulate it locally then use and effect to remove the old and add the new

zackteo 2021-06-20T01:48:00.143900Z

I saw that the todo example does have edit - but that seems to work because the local state is stored in that component

emccue 2021-06-20T02:19:51.144100Z

Don't

emccue 2021-06-20T02:19:52.144300Z

or rather

emccue 2021-06-20T02:20:02.144600Z

lets say your app-db looks like this

emccue 2021-06-20T02:20:29.145400Z

{:some.ns/page-state {:first-name "" :last-name ""}}

emccue 2021-06-20T02:20:47.145700Z

you can write a subscription to get the current page state

emccue 2021-06-20T02:21:12.146300Z

(let [page-state @(rf/subscribe [:some.ns/page-state])]
  ...)

emccue 2021-06-20T02:21:23.146600Z

and then pass that to a component

emccue 2021-06-20T02:22:54.148300Z

(defn info-editor [{:keys [first-name last-name]}]
  [:div
    [input-component {:value first-name}]
    [input-component {:value last-name}]])
  
(let [page-state @(rf/subscribe [:some.ns/page-state])]
  [info-editor page-state])

emccue 2021-06-20T02:23:19.148900Z

and then have events that fire when a user performs an action

emccue 2021-06-20T02:24:48.150600Z

(rf/reg-event-fx
  :some.ns/edited-first-name
  (fn [{:keys [db]} [_ new-first-name]]
    {:db (assoc-in db [:some.ns/page-state :first-name] new-first-name)}))

(rf/reg-event-fx
  :some.ns/edited-last-name
  (fn [{:keys [db]} [_ new-last-name]]
    {:db (assoc-in db [:some.ns/page-state :last-name] new-last-name)}))

emccue 2021-06-20T02:25:18.151Z

and dispatch those events when the user does a thing

emccue 2021-06-20T02:26:11.151900Z

(defn info-editor [{:keys [first-name last-name]}]
  [:div
    [input-component {:value first-name
                      :on-change #(rf/dispatch [:some.ns/edited-first-name %])}]
    [input-component {:value last-name
                      :on-change #(rf/dispatch [:some.ns/edited-last-name %])}]])

(let [page-state @(rf/subscribe [:some.ns/page-state])]
  [info-editor page-state])

emccue 2021-06-20T02:26:35.152400Z

it feels silly for events that just do an assoc, sure

emccue 2021-06-20T02:27:06.153Z

but its a good general model

emccue 2021-06-20T02:27:29.153600Z

and if you need to perform side effects as part of handling an event you can include an :fx key in the map you return

zackteo 2021-06-20T02:28:42.155200Z

so for say an example of a form, i should have events for each of the parameters of that form? Currently I have it saved in a local reagent atom, and only dispatch an event to submit the data when the submit button is pressed

zackteo 2021-06-20T02:29:21.156400Z

This worked. But now im unsure what to do if I want to edit my form data

emccue 2021-06-20T02:29:23.156500Z

i would treat that as an approach to take only when it has proven to be a performance concern

emccue 2021-06-20T02:29:39.156900Z

otherwise just avoid local atoms alltogether

emccue 2021-06-20T02:30:22.157500Z

and if it is just an "assoc" you can cheat a little bit and have one event that is like "update-form-field" that takes a key and a value

emccue 2021-06-20T02:30:32.157800Z

thats a practical compromise

zackteo 2021-06-20T02:31:26.158600Z

but what if i only want to edit the data in the re-frame db when i confirm it ?

emccue 2021-06-20T02:31:47.158900Z

why would you want that?

emccue 2021-06-20T02:31:59.159200Z

or actually

emccue 2021-06-20T02:32:02.159400Z

nvm i see the use

emccue 2021-06-20T02:32:08.159600Z

what i've done in the past is have

emccue 2021-06-20T02:32:25.160100Z

{:form-state .... :editing-form-state nil}

emccue 2021-06-20T02:32:43.160500Z

and when they start editing i copy over the form state and work in the editing form state

emccue 2021-06-20T02:33:02.161100Z

then when they slap submit, put the editing state in form state

zackteo 2021-06-20T02:33:07.161200Z

how do i do that?

emccue 2021-06-20T02:33:42.161800Z

if I am interpreting your semantics correctly, you have a page where the user takes some action that puts them into "edit mode"?

emccue 2021-06-20T02:33:57.162100Z

and then if they click cancel none of what they did applies

emccue 2021-06-20T02:34:04.162600Z

and if they click submit they have edited the form

zackteo 2021-06-20T02:34:07.162700Z

yeah something like that

emccue 2021-06-20T02:34:10.162900Z

okay so

emccue 2021-06-20T02:34:49.163600Z

{:some.ns/page-state 
  {:form-state {:first-name "" :last-name ""}}
  {:edit-state nil}}

emccue 2021-06-20T02:34:52.163900Z

start with this

emccue 2021-06-20T02:35:21.164400Z

(defn editing? [page-state]
  (some? (:edit-state page-state)))

zackteo 2021-06-20T02:37:56.167800Z

i think the part I want to know specifically is I have state (rf/subscribe [:access-constraint id])

(rf/reg-sub
  :access-constraint
  (fn [db [_ index]]
    (get-in db [:constraints index]))) 
How do copy the data over to the :editing-form-state

emccue 2021-06-20T02:39:38.169400Z

(defn info-editor [page-state]
  (if (editing? page-state)
    (let [{:keys [first-name last-name]} (:edit-state page-state)]
      [:div
        [input-component {:value first-name
                          :on-change #(rf/dispatch [:some.ns/edited-first-name %])}]
        [input-component {:value last-name
                          :on-change #(rf/dispatch [:some.ns/edited-last-name %])}]
        [button {:on-click #(rf/dispatch [:some.ns/confirm-edits])} "Confirm"]
        [button {:on-click #(rf/dispatch [:some.ns/cancel-edits])} "Cancel"]])
    (let [form-state (:form-state page-state)]
      [:div
        [display-current-state form-state]
        [button {:on-click #(rf/dispatch [:some.ns/enter-edit-mode])}
                "Edit"])
    
(let [page-state @(rf/subscribe [:some.ns/page-state])]
  [info-editor page-state])

emccue 2021-06-20T02:44:41.174200Z

(rf/reg-event-fx 
  :some.ns/enter-edit-mode
  {:db (update db :some.ns/page-state
              (fn [page-state]
                (assoc page-state :edit-state (:form-state page-state)))))
 
(rf/reg-event-fx
  :some.ns/edited-first-name
  (fn [{:keys [db]} [_ new-first-name]]
    (when (editing? (:some.ns/page-state db))
      {:db (assoc-in db [:some.ns/page-state :edit-state :first-name] new-first-name)})))

(rf/reg-event-fx
  :some.ns/edited-last-name
  (fn [{:keys [db]} [_ new-last-name]]
    (when (editing? (:some.ns/page-state db))
      {:db (assoc-in db [:some.ns/page-state :edit-state :last-name] new-last-name)})))

(rf/reg-event-fx
  :some.ns/confirm-edits
  (fn [{:keys [db]} _]
    (when (editing? (:some.ns/page-state db))
      {:db (update db :some.ns/page-state
              (fn [page-state]
                (-> page-state
                    (assoc :form-state (:edit-state page-state))
                    (assoc :edit-state nil))))

(rf/reg-event-fx
  :some.ns/cancel-edits
  (fn [{:keys [db]} _]
    (when (editing? (:some.ns/page-state db))
      {:db (update db :some.ns/page-state
              (fn [page-state]
                (-> page-state
                   (assoc page-state :edit-state nil)))))

emccue 2021-06-20T02:44:45.174400Z

there we go

emccue 2021-06-20T02:45:00.174800Z

the data is in your app db, you can copy it with an assoc

emccue 2021-06-20T02:45:15.175Z

you see that in :some.ns/enter-edit-mode

zackteo 2021-06-20T02:50:40.176500Z

sorry maybe I should put the code into a page and run it but ... won't :value be left unchanged since the on-change only edits edited-first-name for example? Meaning to say the display won't update as you try to change say the name

emccue 2021-06-20T03:30:28.176700Z

sorry tabbed out

emccue 2021-06-20T03:30:44.177Z

the on change calls edit-first-name

emccue 2021-06-20T03:30:56.177400Z

the view depends on the value of :first-name in the :edit-state

emccue 2021-06-20T03:31:10.177800Z

so when the event fires the view will be re-rendered with the new value

emccue 2021-06-20T03:31:31.178300Z

and then when you click confirm the :form-state will be replaced with the :edit-state

emccue 2021-06-20T03:33:27.178600Z

so thats is what updates :value

zackteo 2021-06-20T04:46:56.181500Z

Right I was wondering specifically where the copying of state was done. And now I see it in enter-edit-mode :) Thanks @emccue for taking the time to answer my question so comprehensively! :D

Oliver George 2021-06-20T23:08:10.187200Z

I think our re-frame event handlers fall into two catgories. 1. User event handler responding to user initiated dispatch (e.g. ::form-save-click) 2. Utility event handlers which provide some stand alone behaviour and often come in pairs (e.g. ::form-save, ::form-save-response) I'm thinking about whether clearly separate those two cases is a good idiom. Q: Which is better... • ui dispatched events mostly just dispatch to utilities (e.g. dispatches to ::form-save) • ui dispatched events do the the work possibly leveraging some pure logic utilities (e.g. calling (form-save-logic-helper)) • something else?

p-himik 2021-06-21T09:45:15.191600Z

I think it's worth specifying that the above "should" is a matter of opinion and not a prescription from the docs. At least, I don't remember the docs saying anything of the sorts. And, obviously, I prefer having util events myself. :)

p-himik 2021-06-20T23:12:18.187700Z

I'd argue that re-frame docs go against either. It's better to have UI-facing events that are intent-based, so ::form-save-click. And to separate the "util" events from the UI events, I simply add - in front of the former: ::-form-save-response.

Oliver George 2021-06-20T23:13:42.188Z

Nice naming convention. I like that.

Oliver George 2021-06-20T23:15:36.188200Z

Would you have a stand alone ::-form-save utility handler or bake that behaviour into the ::form-save-click handler?

p-himik 2021-06-20T23:17:29.188400Z

The latter. I'm not trying to abstract away things that don't have a need for that. I would do that only if ::form-save-click does something in addition to what ::-form-save is doing and is reused somewhere else.

Oliver George 2021-06-20T23:20:52.188700Z

Thanks. I've certainly seen some tortured utilities where different use cases aren't quite the same.