Fork me on GitHub
#re-frame
<
2018-12-19
>
shaken08:12:39

I’m a Re-Frame beginner and could use a bit of a hint. I’ve got an application created with Figwheel and ReFrame10x in my environment, so I am able to watch data flow through the application. The application has a grid of clickable cells, a cell changes color when clicked because the app-db contains a map entry reporting the last cell clicked. The view dispatches when a cell is clicked, an even receives the event and updates the app-db. There is a reg-sub function for the section of the app-db that is updated and the cells have a subscription to the keyword of the reg-sub. Using a combination of ReFrame10x and printlin calls, I have determined that data is flowing correctly through most of the application. Mouse click generates event, event-handler updates the app-db and the subscription notes the change (:sub/run). But the subscribed view does not render. The only time the view seems to render is when I make a change that causes FigWheel to re-load, at which point all of the view components update (not just the subscribed cells view). The item being subscribed to is a map within the app-db. Only one element of this map is changed, but the subscription is tied to the map itself. I’m presuming that this is sufficient, as the overall map identity should change when the new map containing the changed map is created (changed in the app-db, in the persistent structures sense). I’ve been reviewing the breakout application from lambdaisland (https://github.com/lambdaisland/breakout) looking for anything I have missed, but have not been able to figure out the root cause. Does this sound like anything that has been observed in the past? If not, what would you suggest for next-steps? (Perhaps cutting it down to a minimal example for posting here?) Thank you!

restenb08:12:01

could you post an example of a view component that does not update when the subscription changes?

restenb08:12:19

the most common beginner mistake is perhaps that you're somehow not returning a view function, though I believe the latest re-frame no longer differentiates between form-1 and form-2 components, which cuts out the main source of early confusion

danielneal12:12:39

Say I want to do an interceptor to make a change to the db before the handler runs,. Do I put the change to the db in [:context :effects :db] or [:contexts :coeffects :db]

danielneal16:12:32

I ended up setting the db coeffect in :before, then copying it from the coeffect to the effect in :after if the effect wasn't set. Hopefully this isn't too wrong!

manutter5112:12:29

@shaken There’s a quirk in React that sometimes causes it to re-render an earlier version of a component that should be changed. The fix is to set the :key metadata on the component to something that changes when the subscription value changes. So maybe something like this:

(defn cell []
  (let [selected?-sub (rf/subscribe [:cells/selected])]
    (fn []
      (let [selected? @selected-sub]
        ^{:key (str "cell-" (if selected? "selected" "normal"))}
        [:div.cell {:class (if selected? "selected" "")} "Value"]))))

Whiskas12:12:50

How do you guys make it able for the user to open a link in a new tab?

manuel12:12:12

@mateus.pimentel.w I have a route server-side returning the same index.html page as /, and then routing client-side shows the right content for /new-page

Whiskas12:12:35

i mean, i did not explain well haha, for what i understood, people usually switch views by dispatching an “navigate” event, don’t they?

Whiskas12:12:14

i was thinking about adding some kind of href too and prevent the link from being followed when the user uses right click, but allowing them to open that link in a new tab

danielstockton12:12:54

I don't usually use navigate, unless it's triggered from another event. I use pushy which listens to a.click events.

Whiskas12:12:59

but i realized that, because of some wizardry i did not understand yet, it looks that my links are not making http requests anymore and they just switch the view without losing the app state

Whiskas12:12:06

só it’s pushy who is doing this

Whiskas12:12:20

so it’s enough to set hrefs?

Whiskas12:12:27

hahahaha, that’s awesome

danielstockton12:12:48

Pushy triggers a navigate event when it receives an a.click for a matching route

Whiskas12:12:33

i’m using material-ui and i would like to put “hrefs” into ListItems and all sorts of things, do you have some suggestion regarding what to do?

Whiskas12:12:59

i head that there is a “Link” component

Whiskas13:12:11

Just to confirm, the only element i can use to switch views is the aelement or there are other ways?

danielstockton13:12:13

You can fire a navigate event yourself too (e.g. in an on-click), there's nothing necessarily wrong with that. It just isn't the 'default' way i do it for normal links.

danielstockton13:12:00

<a href= is semantically best

Whiskas13:12:09

i get annoyed by using links because they don’t fit everywhere and i have to deal with the css

Whiskas13:12:33

but i need them in order to make the user able to right click and open in a new tab

danielstockton13:12:41

I'm not familiar with material-ui, so my advice might be inappropriate in your case

Whiskas13:12:24

i will see what happens if i use the Link component people are talking about ( i think it is from another library )

danielstockton13:12:29

You could also trigger another event that does (js/window.open url "_blank"). A link isn't the only way to open a new tab

danielstockton13:12:22

It has to be triggered by a trusted event (https://developer.mozilla.org/en-US/docs/Web/API/Event/isTrusted) e.g. a click or swipe. Even then, Chrome (maybe others?) has started blocking those unless the user explicitly allows popups.

shaken15:12:22

@restenb Here is the view component that renders the cells.

manutter5115:12:45

@shaken Is render-cells also a re-frame component? If so you can try returning [render-cells cells-state] i.e. inside [] instead of ()

shaken15:12:33

@manutter51 Thank you, I’ll give this a try. ReFrame10X & println shows it is not rendering at all, except for the first click after a save/figwheel reload. (I.E. it is not rendering an on earlier version). Is the “quirk” you describe a bug? Or is there desirable behavior that is not evident to a new user.

shaken15:12:57

@manutter51 How would I tell if it is a re-frame component? Changing to a vector doesn’t change the behavior.

Braden Shepherdson15:12:58

@shaken is there a reason for the indirection? why not have the caller of cells-state call render-cells directly, and let render-cells contain the subscribe?

Braden Shepherdson15:12:25

so far as I understand it, it matters not where the rf/subscribe call lives, but where the deref/`@` is.

manutter5115:12:54

Wait, I just noticed, you’re never dereferencing the subscription

manutter5115:12:13

(unless you’re dereferencing it inside render cells?)

shaken15:12:00

I am dereferencing in the render-cells. (I actually pulled that into a function for clarity here. The render-cells was an anonymous function previouslt.

manutter5116:12:48

Ok. You can ignore my earlier question about whether it’s a re-frame component — if it’s returning the hiccup vector for the html you want to render, it’s a re-frame component.

manutter5116:12:32

Just for reference, here is how I would typically build something sorta similar to what I think you’re describing:

(defn cell [c selected?]
  [:div.cell {:class (if selected? "selected" "")}
   (:value c)])

(defn cells-grid []
  (let [cells-sub (rf/subscribe [:grid/cells])
        selected-sub (rf/subscribe [:grid/selected-cell])]
    (fn []
      (let [cells @cells-sub
            selected @selected-sub]
        (into [:div.cells]
              (for [c cells :let [cell-id (:id c)
                                  selected? (= selected cell-id)]]
                ^{:key (str "cell-" cell-id (if selected? "-sel"))}
                [cell c selected?]))))))

manutter5116:12:05

assuming each cell looked something like {:id 9 :value "X"}

manutter5116:12:27

and that the [:grid/selected-cell] subscription returned the :id of the currently-selected cell.

danielneal16:12:32

I ended up setting the db coeffect in :before, then copying it from the coeffect to the effect in :after if the effect wasn't set. Hopefully this isn't too wrong!

shaken16:12:56

Thank you, everyone. Pulling the deference outside the re-frame component resolved my issue. (The content of render-cells is a re-frame component. Thank you for the definition, @manutter51.)

mateusz.fiolka19:12:49

Hi, anyone has thoughts on how to implement server sent events (SSE) using re-frame?

mateusz.fiolka20:12:01

I was thinking about using subsciptions to external data (https://github.com/Day8/re-frame/blob/master/docs/Subscribing-To-External-Data.md), but it seems it's not recommended

mateusz.fiolka20:12:04

Ok, maybe not very generic also not pure, but something like that seems to work:

(rf/reg-event-fx
 :attach-to-events-stream
 (fn [_ _]
   (let [stream (js/EventSource. events-url)]
     (set! (.-onmessage stream) (fn [event] 
                                  (let [server-event (read-string event.data)
                                        event-name (keyword (str "on-" (name (:type server-event))))]
                                    (rf/dispatch [event-name (dissoc server-event :type)]))))
     (set! (.-onerror stream) (fn [event]
                                (.close stream)
                                (js/console.error "SSE connection problem" event)
                                (js/alert "Problem with events sources stream connection, restarting")
                                (rf/dispatch [:attach-to-events-stream])))
     {})))

(rf/reg-fx :events-stream-add
           (fn [event]
             (ajax/POST events-url {:body event
                                    :response-format (ajax/raw-response-format)
                                    :handler (fn [e]
                                               (js/console.debug e))
                                    :error-handler (fn [e]
                                                     (js/console.error e))})))