This page is not created by, affiliated with, or supported by Slack Technologies, Inc.
2021-06-20
Channels
- # announcements (1)
- # beginners (17)
- # calva (16)
- # cljfx (5)
- # clojure (94)
- # clojure-europe (1)
- # clojure-italy (2)
- # clojure-spec (4)
- # clojure-uk (1)
- # core-async (12)
- # datahike (3)
- # datomic (6)
- # depstar (4)
- # fulcro (30)
- # introduce-yourself (2)
- # malli (1)
- # off-topic (10)
- # re-frame (71)
- # shadow-cljs (15)
- # tools-deps (3)
- # xtdb (12)
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
I saw that the todo example does have edit - but that seems to work because the local state is stored in that component
(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])
(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)}))
(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])
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
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
i would treat that as an approach to take only when it has proven to be a performance concern
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
and when they start editing i copy over the form state and work in the editing form state
if I am interpreting your semantics correctly, you have a page where the user takes some action that puts them into "edit mode"?
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
(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])
(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)))))
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
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
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?
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
.
Nice naming convention. I like that.
Would you have a stand alone ::-form-save
utility handler or bake that behaviour into the ::form-save-click
handler?
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.
Thanks. I've certainly seen some tortured utilities where different use cases aren't quite the same.
the two cases for me would be user initiated dispatch and "external process" initiated dispatch
so ::user-clicked-submit-form and then, ::form-successfully-submitted. ::form-failed-to-submit
::user-clicked would be initiated by the user, the other two are initiated by the http goblin that lives outside of pure fp
(defn form-save [db]
[[:http-xhrio ...info...]]
(rf/reg-event-fx
::user-clicked-submit-form
(fn [{:keys [db]} _]
{:db (... set loading flag ...)
:fx (form-save db)}))
(defn form-save [db]
{:db (... set loading flag ...)
:fx [[:http-xhrio ...info...]]}
(rf/reg-event-fx
::user-clicked-submit-form
(fn [{:keys [db]} _]
(compose-stuff
(constantly {:db (..set something..)})
form-save)))
Nice. Thanks.