Enables React Native projects written in ClojureScript to use Figwheel's live reloading and REPL.
First you will need to make sure you have ReactNative and its dependencies installed.
On the https://facebook.github.io/react-native/docs/getting-started
page you will want to click the poorly named Building Projects with Native Code
tab, to get instructions on how to set your system up.
Once things are installed you can then follow the instructions below to get a React Native project setup for Figwheel development.
Initialize a project:
$ react-native init MyAwesomeProject
ensure it works by changing into the MyAwesomeProject
directory
$ react-native run-ios # or react-native run-android
Close everything.
Add react-native-figwheel-bridge
:
$ yarn add react-native-figwheel-bridge
Now we'll start a basic figwheel.main project.
Create a deps.edn
file in the MyAwesomeProject
directory:
{:deps {org.clojure/clojurescript {:mvn/version "1.10.339"}
com.bhauman/figwheel-main {:mvn/version "0.2.0"}}}
Create a ios.cljs.edn
file in the MyAwesomeProject
directory:
^{:launch-node false}
{:main awesome.main
:target :nodejs
:output-dir "target/ios"
:output-to "target/ios/main.js"}
Create a src/awesome/main.cljs
file in the MyAwesomeProject
directory:
(ns awesome.main
(:require [react]
[react-native :as rn]))
(def <> react/createElement)
(defn renderfn [props]
(<> rn/View
#js {:style #js {:backgroundColor "#FFFFFF"
:flex 1
:justifyContent "center"}}
(<> rn/Text
#js {:style #js {:color "black"
:textAlign "center"}}
(str "HELLO"))))
;; the function figwheel-rn-root must be provided. It will be called by
;; react-native-figwheel-bridge to render your application.
;; You can configure the name of this function with config.renderFn
(defn figwheel-rn-root []
(renderfn {}))
Edit the index.js
file in the MyAwesomeProject
directory:
// these requires allow you to use the (:require [react]) ns forms and
// and use js/require in general. You must supply all your requires for
// this to work in figwheel
var requires = {};
requires["react"] = require('react');
requires["react-native"] = require('react-native');
requires["create-react-class"] = require('create-react-class');
var figBridge = require("react-native-figwheel-bridge");
figBridge.withModules(requires);
figBridge.start({appName: "MyAwesomeProject",
outputDir: "target/ios",
outputTo: "target/ios/main.js",
mainNs: "awesome.main",
devHost: "localhost"});
Now we are ready to launch our ClojureScript application:
In one terminal you will want to start figwheel running:
$ clj -m figwheel.main -b ios -r
And then start react native building in another terminal:
$ react-native run-ios
When using figwheel-main
figwheel bridge takes care of auto
refreshing the application for you when figwheel reloads code.
You can see this behavior by editing the src/awesome/main.cljs
file. Try changing the "HELLO"
to "HELLO THERE"
. You should see
the application change when you save src/awesome/main.cljs
.
These are the options that you can pass in the configuration Object to
start
.
appName
: (required) the name that you created your React Native
project with.
outputDir
: (required) the same as the :output-dir
that is
configured in your CLJS build.
outputTo
: (required) the same as the :output-to
that is configured
in your CLJS build.
mainNs
: (required) the similar to the :main
that is configured in
your CLJS build, but it must be the JS munged version of the name.
autoRefresh
: (optional) whether the figwheel bridge root element
should auto refresh when figwheel finishes reloading - defaults to true
renderFn
: (optional) the JS munged name of the function that returns
the React elements for your application - defaults to figwheel_rn_root
devHost
: (optional) where the metro bundler is running - defaults to "localhost"
serverPort
: (optional) the port of the metro bundler - defaults to 8081
googBasePath
: (optional) the offset from outputTo
to the base of the goog
libs - defaults to goog/
``
After everything is loaded a figwheelBridgeRefresh
function is registered on goog
.
You can call this function to force the root element to reload.
So for the above example you could set the autoRefresh
option to false.
In index.js
this looks like:
figBridge.start({appName: "MyAwesomeProject",
outputDir: "target/ios",
outputTo: "target/ios/main.js",
mainNs: "awesome.main",
devHost: "localhost",
autoRefresh: false // <-- setting auto refresh to false
});
and control reloading via figwheel.main
's reload hooks
in src/awesome/main.cljs
this looks like:
(ns ^:figwheel-hooks awesome.main
(:require [react]
[react-native :as rn]))
(def <> react/createElement)
(defn renderfn [props]
(<> rn/View
#js {:style #js {:backgroundColor "#FFFFFF"
:flex 1
:justifyContent "center"}}
(<> rn/Text
#js {:style #js {:color "black"
:textAlign "center"}}
(str "HELLO"))))
(defn figwheel-rn-root []
(renderfn {}))
;; adding the reload hook here
(defn ^:after-load on-reload [] (goog/figwheelBridgeRefresh))
It's very similar to the above except you have to refresh manually as described in the last section.
So a minimal project.clj
would look like:
(defproject awesome "0.1.0-SNAPSHOT"
:dependencies [[org.clojure/clojure "1.9.0"]
[org.clojure/clojurescript "1.10.339"]]
:plugins [[lein-figwheel "0.5.16"]]
:cljsbuild {:builds
[{:id "dev"
:source-paths ["src"]
:figwheel {:on-jsload "awesome.main/on-reload"}
:compiler {:main awesome.main
:target :nodejs
:output-dir "target/ios"
:output-to "target/ios/main.js"
}}]})
and your src/awesome/main.cljs
file would need to define an
on-reload
function that calls (goog/figwheelBridgeRefresh)
like so:
(defn on-reload [] (goog/figwheelBridgeRefresh))
This code heavily modified from the figwheel-bridge.js
file in
https://github.com/drapanjanas/re-natal
which in turn was taken from
the now non-existant
https://github.com/decker405/figwheel-react-native
.