ClojureScript, on the other hand, already has a lot of traction among functional programming enthusiasts. While it’s still far from being mainstream, many teams are using it to create web applications because of its amazing features, which dramatically increase developer productivity and result in more concise, compact, and elegant code.
The integration between ClojureScript and ReactJS was one of the distinctive ClojureScript features from the start—and the whole concept of a reactive DOM updates fits Clojure paradigms just perfectly. So, when React Native emerged, quite a few attempts were made to provide an integration between ClojureScript and React Native, too.
The ClojureScript + React Native integration, while offering significant benefits in terms of language features, code readability, codebase size and maintainability, is a bit more complex than using ClojureScript with plain ReactJS, due to more moving parts.
While there are many resources out there for developers using ClojureScript to build React Native apps, including step-by-step instructions on how to set up React Native applications in ClojureScript, these often don’t explain how the integration works, and what you can actually do with it. This article is written to help fill that gap.
React Native: How it works
- Mixed components, which share both traits
The React Native development environment uses Node.js. Most of the time this fact is taken for granted without any proper explanation of why we even need it.
- For bundled applications, this process has already happened during the build.
- What about the development environment? You need something that would transpile your code to JS on the fly before it can be executed by your application.
- So, transpiling, resolving dependencies, and feeding transpiled code and required modules to React Native app is what Node.js actually does when you launch Metro Bundler.
- Last but not least, React and React Native ecosystems use npm (or yarn) as a dependency manager for packages.
ClojureScript turns out to be a perfect match for React, because of its built-in support for Clojure atoms. An atom is, essentially, a state storage container in which every change is isolated from one another.
React, in turn, heavily relies on the application state to selectively re-render specific parts of the DOM. So, integrating the two was quite a natural choice. There are quite a few ways to address this integration:
- Om – https://github.com/omcljs/om
- Reagent – https://reagent-project.github.io/
- Rum – https://github.com/tonsky/rum
This article will focus on Reagent and its usage. Note that the integration of ClojureScript with React is different from the integration with React Native, because of the different application runtimes.
Reagent leverages Clojure atoms to hold the application state and trigger selective components to re-render when needed. To accomplish this goal, it uses its own version of an atom (called a ratom), which keeps track of the changes. When an atom’s contents change, every component that dereferences it is re-rendered.
Component markup in Reagent is based on a Hiccup library, which provides HTML markup using native Clojure vector syntax.
As soon as your application becomes more complex than a typical ‘Hello world’ example, you’ll need to structure it somehow. Various languages provide idiomatic ways to do that, with different frameworks that help you to maintain the common structure throughout the application.
Re-frame does this job in the ClojureScript world. The framework is based on the idea of unidirectional data flow: your application generates events, events change some application states, and user interface must subscribe to those changes to reflect them.
A simple illustrative example:
- User clicks ‘Login’ button and the application dispatches an appropriate event
- Event handler sends an asynchronous request to the server
- Another event handler updates the login state once the request is completed
- The application’s UI component, which is subscribed to login state changes, re-renders the UI accordingly once the login succeeds or fails.
In addition to the above, Re-frame introduces the concept of effects and coeffects, which is essentially a very elegant way of isolating side effects from the functional logic. Re-frame has very extensive documentation worth reading for academic purposes, even if you’re not intending to use it.
Figwheel is a ClojureScript plugin which enables live reloading of the application as soon as you change the code. It does this by watching your files, instantly re-compiling them, then notifying the application that it needs to refresh. One interesting feature here is that the application state is unchanged, so you don’t lose the context you’re currently in.
Figwheel integration with a browser-based application is quite easy. It’s a bit more complex when you are using React Native, which requires either a physical device or simulator/emulator.
Putting it all together
To build a cross-platform app in ClojureScript and React Native, consider starting with this resource. There are links to various integration tools, each one of them solving ClojureScript/React Native integration in its own way.
Note: The combination of ClojureScript and React Native development does not offer you a mature development ecosystem. As of the time of this writing, many things are still highly experimental. React Native upgrades may break things for you, and you’ll need to understand how all the moving parts interact with each other.
Setup example using re-natal and iOS
This example assumes that you’re on OS X and have Xcode already installed. (This is required to run the iOS simulator and Xcode command-line tools.)
Install re-natal from https://github.com/drapanjanas/re-natal (README contains exhaustive instructions).
Once the npm is done downloading all dependencies, you may initialize and start a provided sample app. In most cases it’s going to work out of the box. Default re-natal initialization creates a Reagent/Re-frame application with Figwheel already built-in. It’s recommended to start with a simulator rather than a real device; real device debugging can require some additional network connectivity setup.
When you start your React Native app, a couple of things should happen:
- The native part is built using Xcode command line tools
- Metro bundler (which is responsible for feeding JS modules to your app in dev environment) is started in a separate window
- iOS simulator is automatically launched
- If you have Chrome installed, a Chrome window with React Native debugger is automatically opened
Chrome is used as a debugger by default, but you have other options:
- You can use Safari (although it’s a little less convenient)
- You can download debugging tools specifically designed for React Native: Either standalone react-devtools or React Native debugger. The latter also includes tools for Redux if you need them and provides very useful instructions how to set up a custom debugger for working with React Native (although this is a bit counterintuitive as the debugger selection is configured via Node.js)
- You can use Chrome with a tool like IntelliJ IDEA/Webstorm, which has built-in support for React Native and allows you to see debugging console inside the IDE.
Caveat: As of the time of this writing, the stable version of re-natal supports React Native 0.55.4. This is fairly sufficient for the start. Once you get comfortable with the environment, you may try to upgrade it and fix what’s broken.
If you need more information, you may join http://clojurians.net/ Slack team to stay in touch with an active and very motivated community of other Clojure/ClojureScript developers.
The post Developing React Native applications in ClojureScript appeared first on Upwork Blog.