nosco / hx

A simple, easy to use library for React development in ClojureScript.
MIT License
247 stars 16 forks source link

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

Closed danielneal closed 5 years ago

danielneal commented 5 years ago

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]]))
lilactown commented 5 years ago

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.

lilactown commented 5 years ago

https://github.com/Lokeh/hx/commit/93c43b8fc5cbed372fc8694f6d9b312b9d73dd3b removes the reference of the initial value in the ISwap implementation, fixing this defect.

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

danielneal commented 5 years ago

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