Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Using devcards with re-frame #105

Open
Conaws opened this issue May 11, 2016 · 3 comments
Open

Using devcards with re-frame #105

Conaws opened this issue May 11, 2016 · 3 comments

Comments

@Conaws
Copy link

Conaws commented May 11, 2016

Devcards is awesome, particularly for beginners or anyone exploring Reagent or Om.

Re-frame is a great way to design large Reagent applications -- however, since there is only one app-db that holds all the state, it may not be obvious how you can use it with devcards.

I found an inelegant way to handle it -- and I'm curious if it would make sense to put it in the wiki

I also think that this pr or something like it might allow for a more idiomatic solution #97 curious of your thoughts.

When you want to see the entire state of your re-frame db to appear in a card

require [re-frame.db :refer [app-db]] in the ns, then put that in as the db of your reagent card.

When you want to only see some component of the state,

Def a subscription to that component of the state, and use that as the card's db

Here's an example:

  1. Create a handler to update app-db
(register-handler
 :demo1
 (fn [_ _]
   {:status "great"
     :number 42}))

this just replaces the app-db with the map

  1. dispatch that action
(dispatch [:demo1])

;you can deref the app-db to make sure it worked
@app-db
; should be {:status "great" :number 42}

if you have a card that only needs the status, register a sub for that

(register-sub
:status
(fn [db _](reaction %28:status @db%29)))

;; then def that subscription

(def lildb 
  (subscribe [:status]))

;; now you have a subset of the db you can include in a card

@lildb
;; should show "great"

(defcard-rg status-card
[:h1 (str "I'm" @lildb "today")]
lildb
{:inspect-data true})
`

But wait -- there is a problem with this -- the problem being that changes to the app-db don't get reflected in the def'd subscription

--- in your devcards use that def'd subscription as the card's db, and in place of where you'd define the subscription in your component

Example

(defn swapper []
  (let [s (subscribe [:status])]
    [:div
     (pr-str @s)
     [:button {:on-click #(dispatch [:change-status "fantastic"])} "fantastic"]
     [:button {:on-click #(dispatch [:change-status "terrible"])} "terrible"]]))

(defcard-rg status-card
   [swapper]
  lildb
  {:inspect-data true
   :history true})

when the dispatch buttons are clicked, the value of @s will change, but not the card's db. Why? The card's db isn't being directly altered, or called from within the card -- so if you want it to match up appropriately, you have to do things like this

(def lildb 
 (subscribe [:status]))

(defn swapper []
    [:div
    (pr-str @lildb)
     [:button {:on-click #(dispatch [:change-status "fantastic"])} "fantastic"]
     [:button {:on-click #(dispatch [:change-status "terrible"])} "terrible"]]))

Unfortunately, while doing that will allow you to walk the history back and forth, the history you're walking back and forth can fall out of sync with the actual app-db -- so you have to be cautious.

Is there a better way to do this?

@david
Copy link

david commented Sep 19, 2016

Would it make sense to decouple subscriptions from components? Something like

(defn swapper []
  (let [s (subscribe [:status])]
    #(swapper-view @s)))

(defn swapper-view [status]
  [:div
    (pr-str status)
    ...])

We'd then use swapper-view with the devcard.

EDIT: I was previously naming swapper-view as swapper-component, but I think the former conveys intent more accurately.

@johanatan
Copy link

Would cursors work instead of subscriptions?

@jfigueroama
Copy link

Hello. I made a separate namespace to host a local state version of re-frame v10.2 some months ago. Basically, one creates a state which holds mostly all containers of re-frame (app-db, kind->id->handler, etc.). Then, inside the app, all calls to reg-sub, reg-event-X, subscribe, dispatch, will use the created state as first parameter.

(defonce state
  (-> (new-state)
        (reg-sub :db (fn [db _] db)))
        ...
       (reg-event-db  .....)))
;; later ...
(dispatch state [:event-x])
(subscribe state [:db])

It appears to work well. I can use devcards with 2 re-frame applications on the same namespace/file. It may be useful for someone.

https://github.com/jfigueroama/re-frame

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants