This page is not created by, affiliated with, or supported by Slack Technologies, Inc.
2022-11-06
Channels
- # announcements (3)
- # babashka (1)
- # beginners (26)
- # calva (1)
- # cider (17)
- # clj-commons (16)
- # clj-kondo (11)
- # clojure (21)
- # clojure-europe (9)
- # clojure-norway (1)
- # clojure-portugal (2)
- # clojure-spec (8)
- # clojure-uk (4)
- # clojurescript (35)
- # datomic (5)
- # emacs (9)
- # figwheel-main (15)
- # fulcro (26)
- # honeysql (1)
- # lsp (5)
- # off-topic (2)
- # polylith (1)
- # rdf (6)
- # re-frame (4)
- # reagent (15)
- # reitit (9)
- # releases (2)
- # shadow-cljs (4)
- # sql (25)
- # squint (2)
- # xtdb (7)
hey all… I am having a problem where an effect seems to be firing on every render instead of only when my specified property updates. Here is an example:
(def Demo
(r/adapt-react-class
(react/forwardRef
(fn [js-props ref]
(react/useEffect
(fn []
(js/console.log "resetting options!"))
#js [(aget js-props "options")])
(r/as-element
[:textarea
{:value (aget js-props "value")
:ref ref
:on-change (aget js-props "onChange")}])))))
If I run
(reagent/with-let [state (reagent/atom "face")]
[Demo {:options {}
:value @state
:on-change #(reset! state (.. % -target -value))}])
I see “resetting options!” fire on every change to the textarea.
If instead I do NOT use forwardRef
then there is no problem:
(defn Demo2 [{:keys [value on-change options]}]
(react/useEffect
(fn mount []
(js/console.log "resetting options!"))
#js [options])
[:<>
[:pre value]
[:textarea
{:value value
:on-change on-change}]])
The same snippet above with Demo2
does not trigger the hook. does this have to do with JS object identity vs value?Unrelated to your question, but you should avoid using aget
to get properties out of a JS object. Instead, just use plain interop, as in (.-value ^js js-props)
.
@U2FRKM4TW I’m reading about this and the answer seems to be that because I am dealing with js object equality, I need to use useRef
and convert back to clj
data structures internally to do this: https://www.benmvp.com/blog/object-array-dependencies-react-useEffect-hook/#option-4---do-it-yourself
Maybe! But I don't really have a clue, sorry. I've never really used React hooks. And if I ever start using them heavily enough, I'd probably look at Helix with a potential of using it alongside with Reagent.
(def Demo
(r/adapt-react-class
(react/forwardRef
(fn [js-props ref]
(let [opts-ref (react/useRef (.-options js-props))]
(when (not= (js->clj opts-ref.current)
(js->clj (.-options js-props)))
(set! (.-current opts-ref)
(.-options js-props)))
(react/useEffect
(fn mount []
(js/console.log "resetting options!"))
#js [opts-ref.current])
(js/console.log (pr-str js-props))
(r/as-element
[:textarea
{:value (aget js-props "value")
:ref ref
:on-change (aget js-props "onChange")}]))))))
@U2FRKM4TW makes sense! basically this works great with JUST cljs, except because I am ALSO using forwardRef I’m stuffed back into JS land
React uses reference equality to compare objects passed into the effects deps array
so even tho {}
is the same value each render it's creating a new object with a new reference
yup, had to drill that back into my head
@U4YGF4NGM But {}
is always evaluated to the same object - the PersistentArrayMap.EMPTY
singleton. Where does a new reference come from?
I don't know if that's true in development. if so then we're back to the drawing board
@U2FRKM4TW it comes from the conversion to js in react/forwardRef before it calls my function with props and ref
But maybe I am missing something, will stare in the AM