Props, Children & Component Lifecycle in Reagent
Every now and then I come across the situation that I need to compare previous and next props passed to a Reagent component. Every time again I fail to find some docs and figure it out by trial and error.
Props vs. Children
In React everything passed to a component is
called props
. Children passed to components are
passed as props.children
. In Reagent things are a bit
different and Reagent’s hiccup syntax doesn’t explicitly separate
the two:
;; configuration and one child
[popup {:style :alert} [delete-confirmation]]
;; two children
[popup [alert-icon] [delete-confirmation]]
<Popup style="alert"><DeleteConfirmation></Popup>
In React it is well-defined where you can access the
style
parameter (props.style
) and how
you can access the passed children (props.children
).
In Reagent things are a bit different: you have a function definition which takes a number of arguments which you can just refer to in the same way you can refer to any other function parameter. This makes thinking in functions a lot easier but also overshadows some of the underlying React behaviour.
In a lifecycle handler like
:component-did-update
accessing component arguments
via the symbol they’ve been given in the functions argument vector
doesn’t work:
The moment you define components that are not simple render functions (remember those Form-2 and Form-3 components?) all updates will pass their arguments to the components render function.
The moment you render a component that has been created via
reagent.core/create-class
all updates will pass their
arguments to the :reagent-render
function,
potentially triggering a re-render. The function that returned the
result of create-class
is only ever called once at
the time of mounting the component — your top-level
defn
returns a component instead of being a render
function itself. This is also
why you need to repeat the arguments in the
:reagent-render
arguments.
Props in Lifecycle Handlers
Now how do we get access to these props in a lifecycle handler?
The quick answer is, we use reagent.core/props
—
obvious, huh?
One peculiarity about the props
function is that it
expects the props
data to be the first argument to
your function. Also it has to be a map (if it’s
not props
returns nil
).
If the first argument to your component is not a map all arguments
are interpreted as children and can be retrieved via
reagent.core/children
.
So now we have the props for the current render, how do we access the previous ones? All previously passed arguments are passed to the lifecycle handler. Not as you might think though.
If you have a component that has a signature like this:
(defn my-comp [my-props more] …)
You can access it’s previously passed arguments like this:
:component-did-update (fn [comp [_ prev-props prev-more]] …))
comp
is a reference to the current component. The
second argument which is being destructured here contains what
we’re looking for. As far as I understood the first item is the
component's constructor. The rest are the previously rendered
inputs (again in React they’re all props
, in Reagent
they’re props
and children
).
As you can see you can inspect all previous arguments to a component. The way you access them differs from the default React lifecycle method signatures so hopefully this post helps to clear up some confusion about this stuff. :)