All Projects → flexsurfer → ClojureRNProject

flexsurfer / ClojureRNProject

Licence: other
Simple React Native application with ClojureScript, re-frame and react navigation v5

Programming Languages

clojure
4091 projects
javascript
184084 projects - #8 most used programming language

Projects that are alternatives of or similar to ClojureRNProject

Re Navigate
Example of React Native Navigation with re-frame/re-natal
Stars: ✭ 61 (+90.63%)
Mutual labels:  react-navigation, re-frame
reframe-starter
re-frame starter app. Includes shadow-cljs, deps.edn, garden, re-frame-10x and reitit.
Stars: ✭ 16 (-50%)
Mutual labels:  re-frame, shadow-cljs
learn-re-frame-course-files
🎦 Learn re-frame course files for building Cheffy app
Stars: ✭ 51 (+59.38%)
Mutual labels:  re-frame, shadow-cljs
rn-shadow-steroid
React Native with shadow-cljs on steroids
Stars: ✭ 57 (+78.13%)
Mutual labels:  re-frame, shadow-cljs
SocialApp-React-Native
Social Networking mobile app similar to Instagram in React Native.
Stars: ✭ 79 (+146.88%)
Mutual labels:  react-navigation
think
Instagram clone using React Native with funcional components, React Hooks, React Navigation 4x and Reactotron debugger
Stars: ✭ 20 (-37.5%)
Mutual labels:  react-navigation
breaking-point
BREAKING-POINT lets you quickly define and subscribe to screen (i.e. window) breakpoints in your re-frame application
Stars: ✭ 36 (+12.5%)
Mutual labels:  re-frame
react-native-boilerplate
🚀 A highly scalable, react-native boilerplate reinforced with react-boilerplate which focus on performance and best practices. 🔥. 💻 🚀 😎 👾 👽
Stars: ✭ 82 (+156.25%)
Mutual labels:  react-navigation
delivery-app-mobile
🍕React Native food delivery app
Stars: ✭ 143 (+346.88%)
Mutual labels:  react-navigation
hbb-survey-app
Hatay Municipality Survey Application
Stars: ✭ 18 (-43.75%)
Mutual labels:  react-navigation
sitefox
Node + cljs backend web framework
Stars: ✭ 180 (+462.5%)
Mutual labels:  shadow-cljs
react-native-weather
This project is to explore React Navigation (Drawer, Tab, and Stack Navigators). And explore best practices around styling, design, and collaborating with designers for better UX for building great apps.
Stars: ✭ 51 (+59.38%)
Mutual labels:  react-navigation
tailwind-hiccup
tailwindcss + hiccup = 👍👍
Stars: ✭ 34 (+6.25%)
Mutual labels:  re-frame
roll
RPG dice roller with both Rust CLI and ClojureScript Web interfaces
Stars: ✭ 14 (-56.25%)
Mutual labels:  re-frame
re-frame-utils
Re-frame extensions
Stars: ✭ 59 (+84.38%)
Mutual labels:  re-frame
proffy
React Native + ReactJS + NodeJS project developed on RocketSeat NexLevelWeek. This project is based on an application for connect students and teachers.
Stars: ✭ 30 (-6.25%)
Mutual labels:  react-navigation
shadow-firebase
shadow-cljs and firebase using google auth and persisting state to realtime database
Stars: ✭ 26 (-18.75%)
Mutual labels:  shadow-cljs
re-pressed
re-pressed is a clojurescript library that handles keyboard events for re-frame applications.
Stars: ✭ 150 (+368.75%)
Mutual labels:  re-frame
reagent-shadow-cljs-starter
A minimal starter pack for Reagent-based projects
Stars: ✭ 29 (-9.37%)
Mutual labels:  shadow-cljs
RN-Book-Search
A Book search app using Expo (React Native) and Google Books API
Stars: ✭ 29 (-9.37%)
Mutual labels:  react-navigation

hackmd: https://hackmd.io/@byc70E6fQy67hPMN0WM9_A/rJilnJxE8

Confidence and Joy: React Native Development with ClojureScript and re-frame

Clojure: https://clojure.org/guides/getting_started

Code editor: IntelliJ IDEA Community https://www.jetbrains.com/idea/download/ with Cursive plugin https://cursive-ide.com/

shadow-cljs: http://shadow-cljs.org/ re-frame-steroid: https://github.com/flexsurfer/re-frame-steroid rn-shadow-steroid: https://github.com/flexsurfer/rn-shadow-steroid

PROJECT SOURCES: https://github.com/flexsurfer/ClojureRNProject

1. Create a new React Native Project or open existing one

react-native init ClojureRNProject

cd ClojureRNProject

Open project in IDE

Edit App.js

import React from 'react';
import {
  SafeAreaView,
  View,
  Text,
} from 'react-native';

const App: () => React$Node = () => {
  return (
    <>
      <SafeAreaView>
        <View>
        <Text>Hello CLojure!</Text>
        </View>
      </SafeAreaView>
    </>
  );
};

export default App;

Run the app

Terminal 1: yarn start Terminal 2: yarn ios

OK, now we have RN project and we want to run the same app but with clojure

2. Add shadow-cljs

yarn add shadow-cljs

If you already have it, make sure you are using the latest version

Create shadow-cljs.edn

{:source-paths ["src"]

 :dependencies [[reagent "0.10.0"]
                [re-frame "0.12.0"]
                [re-frame-steroid "0.1.1"]
                [rn-shadow-steroid "0.2.1"]
                [re-frisk-remote "1.3.3"]]

 :builds       {:dev
                {:target     :react-native
                 :init-fn    clojurernproject.core/init
                 :output-dir "app"
                 :compiler-options {:closure-defines
                                    {"re_frame.trace.trace_enabled_QMARK_" true}}
                 :devtools   {:after-load steroid.rn.core/reload
                              :build-notify steroid.rn.core/build-notify
                              :preloads [re-frisk-remote.preload]}}}}

Next, we need to initialize project as Clojure Deps, deps.edn will be used only for code inspection in IDE, if you know a better way pls file a PR

3. Create cljs project

create deps.edn file

{:deps  {org.clojure/clojure       {:mvn/version "1.10.0"}
         org.clojure/clojurescript {:mvn/version "1.10.339"}
         reagent                   {:mvn/version "0.10.0"}
         re-frame                  {:mvn/version "0.12.0"}
         re-frame-steroid          {:mvn/version "0.1.1"}
         rn-shadow-steroid         {:mvn/version "0.2.1"}}
 :paths ["src"]}

Right click on the file and Add as Clojure Deps Project

Optional turn off a spelling

Indellij IDEA -> Preferences

create src folder and clojurernproject package with core.cljs file

core.cljs

(ns clojurernproject.core
  (:require [steroid.rn.core :as rn]))

(defn root-comp []
  [rn/safe-area-view
   [rn/view
    [rn/text "Hello CLojure! from CLJS"]]])

(defn init []
  (rn/register-reload-comp "ClojureRNProject" root-comp))

index.js

import "./app/index.js";

Terminal 3: shadow-cljs watch dev

Reload the app

Disable Fast Refresh

Cmnd+D

Now try to change the code, you should see it reloaded by shadow-cljs in the app

now you have clojurescript RN app configured with hot reload

4. App state with re-frame

To update app state, we need to use events, let's create events.cljs and register our first events

events.cljs

(ns clojurernproject.events
  (:require [steroid.fx :as fx]))

(fx/defn
  init-app-db
  {:events [:init-app-db]}
  [_]
  {:db {:counter 0}})

(fx/defn
  update-counter
  {:events [:update-counter]}
  [{:keys [db]}]
  {:db (update db :counter inc)})

Set cursor on fx/defn and press option+return

Move selection to Resolve .. as... and press return then select defn

To update a view when the state is changed, we need to use subscriptions, let's create subs.cljs and register subscriptions.

subs.cljs

(ns clojurernproject.subs
  (:require [steroid.subs :as subs]))

(subs/reg-root-subs #{:counter})

Now we can update our view

core.cljs

(ns clojurernproject.core
  (:require [steroid.rn.core :as rn]
            [steroid.views :as views]
            [re-frame.core :as re-frame]
            clojurernproject.events
            clojurernproject.subs))

(views/defview root-comp []
  (views/letsubs [counter [:counter]]
    [rn/safe-area-view {:style {:flex 1}}
     [rn/view {:style {:align-items :center :justify-content :center :flex 1}}
      [rn/text (str "Counter: " counter)]
      [rn/touchable-opacity {:on-press #(re-frame/dispatch [:update-counter])}
       [rn/view {:style {:background-color :gray :padding 5}}
        [rn/text "Update counter"]]]]]))

(defn init []
  (re-frame/dispatch [:init-app-db])
  (rn/register-reload-comp "ClojureRNProject" root-comp))

Resolve defview as defn and letsubs as let same way how we did it for fx/defn

you can press "Update counter" button, and then change your code, and you can see app updated, but app state remained the same

now you have clojurescript RN app configured with hot reload and re-frame state

There are three major rules when working with re-frame

  1. views are pure and dumb, just render UI with data from subscriptions and dispatch events

Bad:

(views/defview comp []
  (views/letsubs [counter [:counter]
                  delta [:delta]]
    [rn/text (str "Counter: " (+ counter delta))]
    [rn/touchable-opacity 
     {:on-press #(re-frame/dispatch 
                  [:update-counter (if (> delta 12) 
                                     counter 
                                     delta)])}]))

Good:

(views/defview comp []
  (views/letsubs [counter-with-delta [:counter-with-delta]]
    [rn/text (str "Counter: " counter-with-delta)]
    [rn/touchable-opacity 
     {:on-press #(re-frame/dispatch [:update-counter])}]))

we have a separate subscription and event will get all data from the state

  1. Only root keys should be subscribed on app-db

Bad:

(re-frame/reg-sub :counter (fn [db] (get db :counter)))

(re-frame/reg-sub :delta (fn [db] (get db :delta)))

(re-frame/reg-sub :counter-with-delta (fn [db] (+ (get db :counter) (get db :delta)))

Good:

(subs/reg-root-subs #{:counter :delta})

(re-frame/reg-sub
 :counter-with-delta
 :<- [:counter]
 :<- [:delta]
 (fn [[counter delta]]
   (+ counter delta)))
  1. Events must be pure and do all computations

Bad:

(fx/defn
  update-counter
  {:events [:update-counter]}
  [{:keys [db]}]
  (do-something)
  {:db (update db :counter inc)})

Good:

(re-frame/reg-fx
  :do-something
  (fn []
    (do-something)))

(fx/defn
  update-counter
  {:events [:update-counter]}
  [{:keys [db]}]
  {:db (update db :counter inc)
   :do-something nil})

6. Devtools

let's run re-frisk debugging tool and see what's exactly happening in the app

Terminal 4: shadow-cljs run re-frisk-remote.core/start

and open http://localhost:4567

You can see all that is happening with the app: events, app-db (state) and subscriptions

6. Tests

Add test folder and configure test build in the project

{:source-paths ["src" "test"]

 :dependencies [[...]]

 :builds       {:dev
                {...}

                :test
                {:target    :node-test
                 :output-to "out/node-tests.js"
                 :autorun   true}}}

Let's add some tests

events/counter_test.cljs

(ns events.counter-test
  (:require [cljs.test :refer (deftest is)]
            [clojurernproject.events :as events]))

(deftest events-counter-test
  (is (= (events/update-counter {:db {:counter 0}})
         {:db {:counter 1}})))

And run tests

Terminal 3: shadow-cljs compile test

7. Navigation

React Navigation 5

Terminal 2: yarn add @react-navigation/native @react-navigation/stack @react-navigation/bottom-tab react-native-reanimated react-native-gesture-handler react-native-screens react-native-safe-area-context @react-native-community/masked-view

Terminal 2: cd ios; pod install; cd ..

Terminal 2: yarn ios

core.cljs

(ns clojurernproject.core
  (:require [steroid.rn.core :as rn]
            [re-frame.core :as re-frame]
            [steroid.rn.navigation.core :as rnn]
            [steroid.rn.navigation.stack :as stack]
            [steroid.rn.navigation.bottom-tabs :as bottom-tabs]
            [clojurernproject.views :as screens]
            [steroid.rn.navigation.safe-area :as safe-area]
            steroid.rn.navigation.events
            clojurernproject.events
            clojurernproject.subs))

(defn main-screens []
  [bottom-tabs/bottom-tab
   [{:name      :home
     :component screens/home-screen}
    {:name      :basic
     :component screens/basic-screen}
    {:name      :ui
     :component screens/ui-screen}
    {:name      :list
     :component screens/list-screen}
    {:name      :storage
     :component screens/storage-screen}]])

(defn root-stack []
  [safe-area/safe-area-provider
   [(rnn/create-navigation-container-reload
     {:on-ready #(re-frame/dispatch [:init-app-db])}
     [stack/stack {:mode :modal :header-mode :none}
      [{:name      :main
        :component main-screens}
       {:name      :modal
        :component screens/modal-screen}]])]])

(defn init []
  (rn/register-comp "ClojureRNProject" root-stack))

For hot reload we need to register components differently, we register root-stack as regular not reloadable component rn/register-comp but we use rnn/create-navigation-container-reload for navigation container

After we've required steroid.rn.navigation.events ns we can dispatch :navigate-to and :navigate-back events for navigation between screens

Try to open modal screen and change the code you will see that navigation state isn't changed, the modal screen will be still opened

IMG

КОНЕЦ

Note that the project description data, including the texts, logos, images, and/or trademarks, for each open source project belongs to its rightful owner. If you wish to add or remove any projects, please contact us at [email protected].