Reagent 101 / when does a component re-render?
icncsx
Posted on July 8, 2020
In another post, I alluded to the fact that Reagent components do not re-render if they don't have to. That is true. Where as in React, components re-render all the time as a default behavior, Reagent components do not re-render if their inputs do not change.
This behavior aligns well with functional programming. If the inputs don't change, why should the output change? And if the outputs don't change, why the need to re-render?
Two Conditions
There are two types of inputs (data dependencies) to a Reagent component: r/atom
and props
.
Reagent components re-render if either a Reagent atom used by the component changes or the props to the component change.
if props change
Let's consider an example to make it obvious why a component should re-render if its props change.
Here is a Form 1 component, where name
is a prop.
(defn greet-view ;; render function
[name] ;; prop
[:div "Good morning, " name " !" ])
It's not hard to intuit why greet-view
should re-render if its props (just name
in this case) change. If the name prop is DH, it should render "Good morning, DH!". If the name prop is Edith, it should render "Good morning, Edith!".
If you want to know how props may change over time, check the parent component that is going to call the component. Remember: props flow from the parent. A component cannot get new props unless its parent re-renders.
(def family ["DH", "Edith", "Dani", "Bella"])
(defn greet-view ;; child
[name]
[:div "Good morning, " name " !" ])
(defn family-view ;; parent
[]
[greet-view (rand-nth family)])
In summary, a component will re-render if its parent component re-renders, and the props have changed. In the example above, there is a 75% chance of the greet-view
component re-rendering because we are randomly selecting from 4 possible prop values.
if r/atom changes
It should be noted from the very start that r/atoms are not the same as Clojure atoms. Clojure atoms are not reactive. Use Reagent atoms if you want reactivity.
Speaking of reactivity, I should make it clear what I mean by reactivity. Let's have a look at an example.
(defn counter-control []
(with-let [count (r/atom 0)]
[:<>
[:p "You clicked " @count " times"]
[:button {:on-click
(swap! count inc)}
"Increment"]]))
Here we have a Form 2 component with some local state called count
. On the first-render, @count
is 1, which means that the paragraph will read "You clicked 0 times."
Suppose that you decide to click on the button, which changes the r/atom (increments the count). In that case, Reagent will re-render the component because it knows that the r/atom has changed and we are also dereferencing the atom (i.e. actually using the atom). If we don't use it, why re-render, right?
Mental Checklist
If your component does not re-render, don't panic. Check props. Do they change? If not, check r/atoms. Do they change anywhere and if so are we dereferencing them in the component that you expect to re-render. Ok, cool! You're good to go.
Warmly,
DH
Posted on July 8, 2020
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.