I have been playing with clojure for the past several month and like it so far. Figwheel is a miracle, lein is a great swiss knife for developers with many plugins for organising the process and dev/prod environments.

In this article I will show some basic principles of Reagent (clojure script wrapper for React ) and java script/clojure script interoperability on one particular example from real life.

The Problem

A web page with pretty typical images slider on it has specific feature — it has a gif image as one of its slide and the gif must always start from the beginning when the slide is active.

The Solution

There is a bunch of libraries providing gif playback control, but as engineers 😎 we always try to avoid introducing extra dependencies. So, the only choice left is to force browser to reload the image every time the slide is active.

This can be achieved by recreating img node or simply resetting its [src] property. In both cases browser will load the image once, and then render it from cache every time img[src] is set to image path.

Implementation

You can find the code on github https://github.com/gvidon/fancy-gif

For this simple app we will use UI framework called Reagent and css framework UIkit. The latter one includes images slider script which will be used in the app, it will save a lot of time from writing own implementation.

In order to start we need the simplest Reagent app like this one https://github.com/reagent-project/reagent/tree/master/examples/simple Reagent is React wrapper and thus its API is pretty similar to React.

Create main component:

And then render it into DOM-node:

Do you miss jsx? You are not alone. To be honest this is the weirdest part of Reagent. I get used to jsx and it seems so natural compared to Hiccup structures.

The implementation will rely on UIkit images slider script. It allows to describe every slide in a distinct node, also it puts .ui-active class on currently active node. We will track presence of this class and will set up img[src] accordingly:

to the gif-image path when the slide is active

and to the empty string in other cases.

Let’s observe some mutations and change some state

At the core of the implementation there is Mutation Observer API. This thing will let our code to know every time requested attribute of the node changes. In this case — it [class] attribute. Let’s start:

What this code does is instantiating MutationObserver object and passing in callback function, which checks if the [class] of the node changed to uk-native (active slide) and then updates the state.

If you are new to clojure script you must have questions about these js/MutationObserver . and swap! things. The first one is the way to call java script API from within clojure script. You should definitely read this article in order to get familiar with interoperability of clojure script and java script.

swap! is the way to manage Reagent state. It replaces current value of the state with var with the value returned by callback function. State vars are defined as Reagent atom objects, changing them will force dependent components to rerender:

After we have Mutation Observer object instantiated it is time to apply it. This should be done after component is fully mounted and has its DOM tree rendered, so that observing DOM node can be found in the tree and passed to observer. Reagent allows to utilise React class API methods for this purpose:

As you can see while applying observer object to DOM node we request it to only observe changes in [class] attribute. Within render method the state var is checked and based on its value img[path] is updated. Finally component-will-unmount disconnects observer, which means it no longer reacts on changes in DOM. That is it! Every time slide gets active the image inside it is reloaded.

Conclusion

Clojue script is not main stream (yet?). Compared to java script it has much smaller community, much less stars on github and its libraries get updates less often.

But as you can see clojure script is mature enough to start using it in real life projects. We were able to build simple Reagent (React) app with manageable state. It even calls some exotic API (I bet you don’t deal with DOM mutation on a daily basis).

For apps bigger than this example you will need more tools and you will almost always find them. There is side effects library called Reframe which allows to deal with any kind of side effects in a declarative manner. Xhr client for performing http requests from browser. Routing library which is called bidi and many others.

I highly encourage you to start playing with clojure script, it is worth it!