reagent-project / reagent-template

A Leiningen template for projects using Reagent.
MIT License
394 stars 55 forks source link

Component from other ns doesn't see figwheel updates if it is stored in state atom #9

Closed rsslldnphy closed 9 years ago

rsslldnphy commented 9 years ago

This isn't strictly about the template, but it appears to be caused by some weird intersection between how figwheel and reagent work, so asking here - happy to ask elsewhere if there's a more appropriate place.

I've been trying to get figwheel working with my reagent project based on this template, and have been bitten by a strange edge case which I've finally managed to narrow down. I have an example project to demonstrate it here: https://github.com/rsslldnphy/reagent-figwheel-issue (apologies for the weird naming in it, I've been struggling with this all day and it's sent me a bit round the bend) but I'll do my best to describe it here too:

It happens when your page components are defined in separate namespaces. You import them into the main namespace and set up the routes to assoc them into the app-state as :current-page - which works as expected. However, when you make changes to the page component and save the file, although figwheel successfully sends the updated file to the browser, you don't see your changes.

I think this is because the var in the app-state is still the old function - it's never looked up again so the new code isn't seen. If the page component is in the namespace that is reloaded then it works no problem. If you define a component that simply wraps the page component in the other namespace, and put THAT into the app-state, then it works. But a component from another namespace, directly in the app-state, doesn't see updates.

In the example project I've linked above, I have two lines that describe the smallest change I could find to manifest the problem. Comment in the first one and you'll won't see updates to the bar/fish component. Comment in the second one and you will.

(defn barfish [] [bar/fish])
(secretary/defroute "/" []
  ;;(put! :current-page bar/fish)) ;;<--- doesn't update when contents of bar/fish changed
  (put! :current-page barfish))    ;;<--- does update when contents of bar/fish changed

This might just be my limited experience with Reagent and Figwheel - but should this be something that should work or could be made to work?

holmsand commented 9 years ago

This is an interesting problem, and it lies with Clojurescript itself. Clojurescript functions (like javascript's) are passed by value, and not by reference (which would have solved your problem).

There are two solutions: either re-run defroute whenever figwheel reloads (so that it gets the updated version of bar/fish), or provide a level of indirection, as you've found out.

rsslldnphy commented 9 years ago

Thanks, that's useful info. I figured it was something like that. I've got it working with something along these lines, which seems to work.

(defonce current-page-location
  (atom [:cats :index]))

(defn change-page!
  [& ks]
  (reset! current-page-location ks))

(defn current-page
  "Get the current page." []
  (get-in
    {:cats  {:index cats/index
             :new   cats/new
             :show  cats/show}
     :dogs  {:index dogs/index
             :new   dogs/new
             :show  dogs/show}}
    @current-page-location))
jashmenn commented 8 years ago

For anyone visiting this page in the future:

I was having a similar problem and I was able to fix it by making sure I was calling (accountant/dispatch-current!) in my reload function and it worked, although it's not entirely clear why this fixes the problem.