Meta

Decorator Components with ReactJS

Today I'd like to talk about a composition pattern, which can be best denominated as Decorator. Like in object oriented programming the decorator wraps a component and extends its functionality, usually without changing the basic behaviour. With the composition capabilities of ReactJS it is possible to adopt this concept and allows to create components that extends other components, for example by visual effects, or pure functional behaviour like logging activities. In this article I'll present a simple use case demonstrating the pattern.

CSSTransitionGroup - An addon for animated components

A nice example for a decorator is an animated component. The idea is to let an item appear with animation. In this case, this is simple as we can use an addon component from React: CSSTransitionGroup.The CSSTransitionGroup is very easy to use. Just pass a name for a css class and put the component to be animated inside the CSSTransitionGroup. Furthermore, you need to define the CSS to be transitioned, for example:

Creating a Logger decorator

So, that's the way a decorator can be used. How we can create such a decorator? While the CSSTransitionGroup is a bit too complex to show off here, I'll choose another use case where the decorator pattern could be applied. Let's create a generic logging component, which logs information about a child component. The idea is to wrap any component with our decorator we want to log, i.e. for development purposes. The usage is like this:

Our Logger should be capable to log property and state changes for example to the console. Additionally, we want the logger returning each log event for custom purposes. Probably, the latter is the easiest thing: just pass a function as property, which will be our callback. For our logger it should be sufficient to inform about mounting, updating and unmounting a component. So a first stub may look like this:

Well, this Logger resolves a good part of our requisites. A typical pattern for decorators is returning its wrapped component in the render function directly. As we do not render the logger itself, but only the coupled component, we return the child component using the special ReactJS property .children. To pass a log event up the component hierarchy we simply use the onLog property for propagation. This is the pure React-way. As we can see, we use three of the life cycle functions for logging. But hey, what about componentDidUpdate() method? And what about the properties and the state? Wait, one after another.

We could override the Loggers componentDidUpdate() method maybe like this:

This is quite a good idea, and would work; at least for the properties, but this won't work for the child's state. Because the state is not as easy accessible as properties are, i.e. there's is no this.props.children.state. This is by design, and it's good design. Usually, we should not access a child's state. In our case it is viable to access it. The way to get a state of a child is to intercept the child's life cycle method, i.e. componentDidUpdate(). We need to implement a hook. And this works like this:

In this case, we hooked in at mounting phase already. The magic is the this.props.children.type.prototype object. There we can hook into the components methods. The changing state and properties are passed as arguments to the componentDidUpdate() function. This is, how we access the state and properties, each time when the children component is updated. One detail we need to consider. We need to reset the component behavior when the Logger (and the child) gets unmounted. That's why a backup of the original function is necessary. And here comes the resetter: