Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Question about rendering performance when updating state asynchronously #236

Closed
Jarzka opened this issue Apr 3, 2021 · 9 comments
Closed

Comments

@Jarzka
Copy link

Jarzka commented Apr 3, 2021

I have the following example component. It depends on three different atoms, and it contains a button which updates all atom values asynchronously.

(def value1 (atom 1))
(def value2 (atom 10))
(def value3 (atom 100))

(rum/defc render-example < rum/reactive []
  (println "Rendering example")
  [:<>
    [:div (str "Values: " (rum/react value1) ", " (rum/react value2) " and " (rum/react value3))]
    [:button {:on-click (fn []
                         (go
                           (swap! value1 inc)
                           (swap! value2 inc)
                           (swap! value3 inc)))}
      "Increment all (async)"]])

Now when I press the button, the component is rendered three different times ("Rendering example" is printed three times). Why is that? I would have guessed that when I press the button, all the atoms are updated and only after the asynchronously event has been completed, the component would render itself once.

It works if I do the update synchronously (without go block). In that case the component is rendered once. But let's say I absolutely need to do the update asynchronously. Why is the component updating three times in this case?

@Jarzka Jarzka changed the title Question about rendering performance Question about rendering performance when updating state asynchronously Apr 3, 2021
@Azzurite
Copy link
Contributor

Azzurite commented Apr 3, 2021

And whatever the answer for "why is it updating 3 times" is, I'd personally also like to know how to avoid this :)

@roman01la
Copy link
Collaborator

This is a React question. React batches updates scheduled synchronously in React-controlled phases, such as event handling in this case. When called outside of such controlled phases, such as asynchronously in an event handler in this case, React is not able to batch scheduled updates, thus component update is triggered for every scheduled update. For those cases one can use React's batching API (batchedUpdates), to explicitly batch updates that are out of React's control

@Jarzka
Copy link
Author

Jarzka commented Apr 4, 2021

That's interesting, because the same example in Reagent only renders the component once, even if I update the atoms asynchronously:

(def value1 (r/atom 1))
(def value2 (r/atom 10))
(def value3 (r/atom 100))

(defn render-example []
          (println "Rendering example")
          [:<>
           [:div (str "Values: " @value1 ", " @value2 " and " @value3)]
           [:button {:on-click (fn []
                                 (go
                                   (swap! value1 inc)
                                   (swap! value2 inc)
                                   (swap! value3 inc)))}
            "Increment all (async)"]])

I wonder if Reagent has done some optimisations when multiple Reagent atoms are being updated, so that the component is not rendered between the updates.

@roman01la
Copy link
Collaborator

roman01la commented Apr 4, 2021

Yes, Reagent is using its own scheduling and processing queue, Rum used to have that as well, but that was changed to be closer to React, be compatible with concurrent mode and avoid issue with input fields

@Jarzka
Copy link
Author

Jarzka commented Apr 5, 2021

Okay, good to know. Thanks for the answer.

@Azzurite
Copy link
Contributor

Azzurite commented Apr 5, 2021

The thing though is, how is it possible to batch your own updates when using the reactive mixin? Wouldn't the mixin need to do that? It'd be a bit weird if in fact the solution is to program your own reactive mixin.

@roman01la
Copy link
Collaborator

batching happens on caller's side

(react-dom/unstable_batchedUpdates
  (fn []
    (swap! value1 inc)
    (swap! value2 inc)))

@Azzurite
Copy link
Contributor

Azzurite commented Apr 5, 2021

I see, thank you very much :) I didn't understand how that feature worked when reading about it.

@Azzurite
Copy link
Contributor

Azzurite commented Apr 6, 2021

@Jarzka is this closeable?

@Jarzka Jarzka closed this as completed Apr 6, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants