No, this won't work as you expect. To understand the difference, consider the following two components:
(defn test-component-if []
(let [a (atom 1)
_ (.log js/console "let in -if")]
(if (odd? @a)
[:div
[:p "odd"]
[:button {:on-click #(swap! a inc)}
"inc"]]
[:div
[:p "even"]
[:button {:on-click #(swap! a inc)}
"inc"]])))
(defn test-component-fn []
(let [a (atom 1)
_ (.log js/console "let in -fn")]
;; I dub thee test-inner
(fn []
(if (odd? @a)
[:div
[:p "odd"]
[:button {:on-click #(swap! a inc)}
"inc"]]
[:div
[:p "even"]
[:button {:on-click #(swap! a inc)}
"inc"]]))))
test-component-fn
works as expected, while test-component-if
does not. Why is that? Well when a reagent component can return one of two things (I'm ignoring "type-3" components, as that hooks into react knowledge). It can return
- a vector
- another function
If it returns a vector, the function itself becomes the render function, in our case test-component-if
. When it returns a function, the function that was returned, not the original function, is the render function. In this case, what I have dubbed test-inner
When Reagent calls a render function, it tracks the atoms that function accesses, and whenever that atom changes it calls the render function. So what happens when we use test-component-if
?
- Reagent calls
test-component-if
- Our let clause binds a new atom
a
to 1.
- A vector is returned
- We click the button
- The atom
a
is incremented
- Reagent sees the change to
a
and calls test-component-if
- Our let clause binds a new atom
a
to 1. (A different atom than our previous a
)
- Ooops!
So a
is always 1. You can verify this by looking at the console, and seeing that the message is printed every time you click the button.
Now how about test-component-fn
?
- Reagent calls
test-component-fn
- Our let clause binds a new atom
a
to 1.
test-component-fn
returns test-inner
which closes over a
- Reagent calls
test-inner
- We click the button
a
is incremented
- Reagent sees the change to
a
and calls test-inner
- Repeat as many times as you want.
You can verify that let only gets executed once again on the console. Click the button as many times as you want, the message will only be printed when it's first rendered.
In terms of an if
without an else
clause, this will indeed return nil. It's convention to use when instead of if
in such cases, which makes it clear the lack of an else
is intended. It also has the advantage of including an implicit do. As for what Reagent will do when it encounters that nil
, in most cases it will silently remove it and display nothing.