nosco / hx

A simple, easy to use library for React development in ClojureScript.

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

swap! on a <-state atom behaves differently from useState

danielneal opened this issue · comments

I wasn't sure exactly how to explain this - it was an issue that came up as I was trying to implement drag and drop and had quite a lot of event handlers all over the place.

I've done my best to make a minimal reproduction, hope it's not too vague!

The component below sets up a timer which increments a counter.
The raw react version increments the number on every tick, it calls the version of setNum that takes a function. The hx version only increments once, but doesn't continue to increment, I think because it is holding a reference to the initial state.

(defnc Counter
  [opts]
  (let [num-hx (<-state 1)
        [num setNum] (react/useState 1)]
    (react/useEffect (fn []
                       (let [id (js/setInterval #(swap! num-hx inc) 1000)]
                         (fn []
                           (js/clearInterval id)))) #js [])
    (react/useEffect (fn []
                       (let [id (js/setInterval #(setNum inc) 1000)]
                         (fn []
                           (js/clearInterval id)))) #js [])
    [:div
     [:span @num-hx]
     [:span "/"]
     [:span num]]))

I had encountered this before and thought I was using it wrong. Your example was helpful in that it illustrates the difference between useState and <-state.

I believe the problem is that the implementation of ISwap has an implicit dependency on the current value in the Atomified instance. When your initial closure is created with useEffect, it captures that initial value in the atom instance and never gets the updated value.

93c43b8 removes the reference of the initial value in the ISwap implementation, fixing this defect.

I'll cut a release later tonight with this change.

Nice! Thanks - I'll try it out in my full example but this fix looks good :)