In general you probably want fast reference equality w/ functions, so I would not want to mess with redefining its IEquiv method.
The OP & replies are very interesting in exploring different ways to handle problems of equality in ClojureScript. However, I would again recommend people to use react/useCallback
or react/useMemo
for these purposes as it’s exactly what they’re made for.
example:
(react/useCallback
(fn on-change [ev]
(do-thing-with localA localB ev))
#js [localA localB])
When used inside the body of a component, this expression will return the callback passed to it. The reference will only change if localA
or localB
change. This is precisely what you want when rendering components which are memoized (like reagent’s are by default) or other memoized values.
The way they work is by storing the callback & the array of values on the component instance. If you imagine you have a simple component tree like:
app
/ \
componentA componentB
| |
input button
|
"click me"
When instantiated, this is represented roughly like (psuedo-code ahead):
{:type app
:state {,,,}
:props {,,,}
:children [{:type componentA
:state {,,,}
:props {:on-change #object[Function on-change] ,,,}
:children [,,,]}
{:type componentB
,,,}]}
Now let’s say that app
uses useCallback
to memoize the on-change
prop passed to componentA
:
(defn app [{:keys [localA localB]}]
(let [on-change (react/useCallback
(fn on-change [ev] ,,,)
#js [localA localB])]
,,,))
What this does is inform React to store the (fn on-change [ev] ,,,)
value and the array [localA localB]
on first render on the instance, something like:
{:type app
:state {:hooks [{:type useCallback
:fn #object[Function on-change]
:deps #js [localA localB]}]}
And on each subsequent render, it will do a comparison between the values of the array in :deps
to determine whether it should re-use the one stored on the component instance, or use the freshly created one.
Note that this strategy also allows us to ignore values or even pass in values that aren’t used, but semantically should bust the reference identity, which in practice can be quite helpful when tuning render behavior & performance.