How to combine figwheel, Om and Ring in one application
In this article you will find a configuration of figwheel with Om and a Ring server in one application.
The following issues will be addressed:
- Om root component will be remounted upon code reload
- App-state is lost when code is reloaded
- Code changes in other cljs files do not cause a re-render
Let’s get Om with it
For a few weeks I have been using Clojurescript and Om for front end development. Om is a Clojurescript library based on the famous React. Figwheel is a tool that can speed up Clojurescript and Om development by reloading code in the browser without refreshing an entire page.
The project I’m currently working on consists of several Clojurescript files. Often an Om component resides in a file of its own. For my workflow it would make sense to be able to edit a component in one file, save the code and see the change in the browser immediately. So here is what I’ve done.
Let’s make a project based on the wrom template.
1 2 3 4 5 6 7 8 9 10 11 12 13
The application comprises a server and client part. All sources files
reside in one directory
src. Let’s add figwheel to our project. Add
[figwheel "0.1.4-SNAPSHOT"] to
[lein-figwheel "0.1.4-SNAPSHOT"] to
should now look like this:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33
client.cljs. In the namespace declaration under
[figwheel.client :as fw]. Now we’ll hook up figwheel
so it can send changes in our project to a browser. Because we use
Ring with the Jetty adapter as an external server, we have to tell
figwheel explicitly where it’s websocket is, since it can’t just
connect to the same origin.
1 2 3 4 5
Start Ring and Figwheel independently, both from inside the
directory. In one terminal type
lein figwheel (or optionally supply
the name of the build:
lein figwheel example). It’s best to wait for
the clojurescript compilation to complete before starting Ring.
1 2 3 4 5 6 7 8 9
In another terminal type
lein ring server. If you’re lucky a browser
will open automatically to
localhost:8090/index.html and you will
see the text
Hello world from server!. This text really came from
the Ring handler in
server.clj. If you open a developer console, you
will see that figwheel has connected to the browser:
Now let’s see if figwheel will pick up a code change in
client.cljs. Let’s change the
render function to:
1 2 3 4 5
If everything worked, in the browser you will almost immediately see that your change is reloaded and re-rendered by Om.
Om root component will be remounted upon code reload
One problem of making changes in the file where the call to
resides is that the entire component tree will be unmounted and
replaced by a new instance. To verify my claim, let’s add line 6 and
19-21 from the snippet below:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
Now make a change anywhere in the source code and save the file. In
the browser’s console you will see that Om mounted the
component. Now change the source again and press save. You’ll see that
Om unmounted the component and mounted a new instance of the
component. You really have to pay attention to this, because if you
created resources or
go loops in
will-mount and you didn’t clean
them up in
will-unmount, things can get really messy when you have
lots of code reloads. The
go loops from unmounted instances will
just keep running and your program can get unpredictable. So, the
solution to this problem is to keep track of your resources and take
the appropriate actions in the
App-state is lost when code is reloaded
Let’s change our app state to
and the render function to
1 2 3 4 5 6 7
In the render function we show a printed version of the cursor. Now
let’s click the button. The
app-state updated and the component is
reflecting this. Now let’s change the code of
save. What do we see? Our app-state is back to the initial state. This
app-state is being redefined by the code reload. To avoid
Now the app-state will survive a code reload. Sometimes it’s that easy to write re-loadable code.
Code changes in other cljs files do not cause a re-render
Let’s make a second file called
child.cljs like this:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
om/build this child component in our root component, so it will
appear on the page. I omitted irrelevant parts.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
I’m not entirely sure if figwheel handles changes in namespace declarations well, so just refresh the page. You will see the button from the child component on the screen. Click a few times:
Now let’s change some code of the child component. For example, change
the text in the button to “Click me to update me!”. When saved, you
won’t see the change reflected in your browser. figwheel has reloaded
child.cljs but the problem is that Om doesn’t ‘know’ about this. Let’s
tell Om. Let’s summon the power of
:require entry for
[cljs.core.async :refer [<! chan put!]] so we can create channels
and put something in them.
In client.cljs add a channel. Place it below the definition of
will-mount we’ll now spawn a
go loop that keeps reading from
1 2 3 4 5 6
All we have to do now is put a (truthy) message into the channel and
the root component will re-render itself. This can be done in the
callback provided to
1 2 3 4 5 6
Refresh the page so everything is in place. Now change the text of the button again in child.cljs and you’ll see the component being instantly re-rendered in the browser. Observe however that all local state in the child component is lost. Because the code of the child component changed, Om has to remount the component. Again, be conscious of resources and go loops hat are creating during the mount phase and clean them up if necessary.
Let’s change to child to show and update the cursor instead of local state.
1 2 3 4 5 6 7 8 9 10 11
Click a few times and you’ll see something like this:
Now change the text of the button in child.cljs again and save. You’ll see that the state of the cursor is preserved, as expected.
Update October 11th 2014
The use of a channel to refresh a component was a little invasive and to be honest, I didn’t like it much. Arne Brasseur pointed out in the comments that refreshing of Om components can be implemented simpler, because
om/root is idempotent. This means it may be called multiple times and all will be well.
I changed the code in
main.cljs according to the suggestion of Arne:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36
- no invasive channel and call to
- the call to
om/rootis now wrapped inside a function
mainis called on initial page load and will be called by figwheel upon code reload (no matter which ClojureScript file, which the point of putting the call to
Arne Brasseur is the author of the excellent Chestnut template, that embeds Figwheel as one of its dev tools. I suggest you try it out if you haven’t. David Nolen, the author of Om, just published a demo video of Chestnut.
The completed (and updated) code of this blog post can be viewed here.
If you liked my post or want to suggest an improvement, please leave a comment. Thanks for reading!