lilactown / helix

A simple, easy to use library for React development in ClojureScript.
Eclipse Public License 2.0
624 stars 52 forks source link

gensym / anonymous function syntax breaks fast refresh #117

Open SevereOverfl0w opened 1 year ago

SevereOverfl0w commented 1 year ago

Simple reproduction that will cause fast refresh to always do a full reload:

(defnc ...
  (hooks/use-effect
    []
    (run-my-callback #(identity %)))
  ...)

It will be fixed by using (fn [%] (identity %)) instead. The problem is that on each reload the gensym'd binding that replaces % is changed. Compare:

(hooks/use-effect [] (reitit.frontend.easy/start! (reitit.frontend/router []) (fn* [p1__27869#] (identity p1__27869#)) {:use-fragment false})) 
;; vs
(hooks/use-effect [] (reitit.frontend.easy/start! (reitit.frontend/router []) identity {:use-fragment false}))

I don't have an clear idea on how to best fix this. If there was a way to detect & remove gensym'd symbols, that would be ideal. I don't know think that's possible at all.

The anonymous function syntax seems likely to be the most common (only? is this something that can only happen from syntax, or could a macro cause it too?) cause of this and it will always end its binding with #, or you could pick up on the prefix of p\d+__ for that case, too.

lilactown commented 1 year ago

Thanks for the report.

I haven't tested this, but since it's the defnc macro that does the fast-refresh detection, I think this is only a problem for things that gensym new bindings in the reader. E.g. if you have a macro that gensyms some binding,

(defnc my-component
  []
  (hooks/use-effect [] (my-thing-that-generates-some-bindings-internally))
  ,,,)

The code used by defnc will be the non-macro-expanded forms. It's only because the anonymous function syntax expansion happens in the reader that this is a problem. I think.

Based on my reading here https://github.com/clojure/tools.reader/blob/fb185af411fa379e48e5d0a9e623b68f552d8c92/src/main/clojure/clojure/tools/reader.clj#L528-L532 I think that detecting the # at the end of the binding seems easiest.

PR welcome!

roman01la commented 10 months ago

FYI I had a similar issue in UIx, here's the fix, perhaps it can help https://github.com/pitch-io/uix/commit/7c39a4e38b7d665f99d727127cdb6e8d376d66e7