loading...
Cover image for Reagent 101 / when does a component re-render?

Reagent 101 / when does a component re-render?

icncsx profile image DH Kim ・2 min read

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 by:

icncsx profile

DH Kim

@icncsx

Software engineer interested in cloud computing and serverless backend services.

Discussion

markdown guide