reagent-project/reagent

ratom update within render function does not cause a re-render

john-shaffer opened this issue · 2 comments

Reagent version: 1.1.0
Repro:

(defn DocsBad []
  (let [state (r/atom {:versions nil})]
    (fn []
      (let [state* @state
            {:keys [versions]} @state
            version-entity-ids [1]]
        (when (not= versions version-entity-ids)
          (swap! state assoc :versions (vec version-entity-ids)))
        [:div (pr-str state* @state)]))))

Expected: Component renders at least twice and results in [:div "{:versions 1} {:versions: 1}"].
Actual: Component renders once and results in in [:div "nil {:versions 1}"] (unless something else causes a render).

I tried using this component in a project, and I'm seeing {:versions [1]} {:versions [1]} rendered. I also tried adding some console.logs on the fn body and inside when form, and I can see the update is called once and render is first called with {:versions nil} and then with the updated value.

If I run the code which @john-shaffer provided as is (reproduced here for clarity)...

(defn DocsBad []
  (let [state (r/atom {:versions nil})]
    (fn []
      (let [state* @state
            {:keys [versions]} @state
            version-entity-ids [1]]
        (when (not= versions version-entity-ids)
          (swap! state assoc :versions (vec version-entity-ids)))
        [:div (pr-str state* @state)]))))

...I get {:versions nil} {:versions [1]}. The view component is being rendered once.

It may help to share that if I run the following modified code...

(defn DocsBad []                                                                                    
  (let [state (r/atom {:versions nil})]                                                             
    (fn []                                                                                          
      (let [state* @state                                                                           
            {:keys [versions]} @state                                                               
            version-entity-ids [1]]                                                                 
        (when (not= versions version-entity-ids)                                                    
          (r/next-tick (partial swap! state assoc :versions (vec version-entity-ids))))             
        [:div (pr-str state* @state)]))))

...I get {:versions [1]} {:versions [1]}. The view component is being rendered twice.

Note that the difference merely involves using the next-tick Reagent function, so that the swap! is the tiniest bit deferred.