Slide 1

Slide 1 text

! 1 Multi-platforms apps with reason-react-native

Slide 2

Slide 2 text

! 2 Mathieu Acthernoene @zoontek

Slide 3

Slide 3 text

! 3 ⏱ Disclaimer 30 min is short I will be brief on non-primary points

Slide 4

Slide 4 text

! 4 React Native A mobile framework to build user interfaces Supported platforms : iOS, Android & UWP

Slide 5

Slide 5 text

! 5 React Native Credit : Formidable labs UIView android.view.View

Slide 6

Slide 6 text

! 6 ReasonML Alternate syntax + tooling for OCaml Looks like modern JavaScript

Slide 7

Slide 7 text

! 7 let a = 1; /* int */ let b = 1.0; /* float */ let c = "Hello"; /* string */ let d = 'Z'; /* char */ let e = true; /* boolean */ let f = [1, 2, 3]; /* list(int) */ let g = [|1, 2, 3|]; /* array(int) */ let h = Some(x); /* option('a) */ let i = (); /* unit */ ReasonML

Slide 8

Slide 8 text

! 8 type user = { name: string, age: int, }; let me = { name: “Matthieu", age: 27, }; let meLater = { ...me, age: 28, }; ReasonML

Slide 9

Slide 9 text

! 9 ReasonML let sayHi = name X> Js.log("Hi " Z[ name Z[ “!”); let add = (a, b) X> a +. b;

Slide 10

Slide 10 text

! 10 BuckleScript OCaml to ES5 JS compiler Supports both syntaxes

Slide 11

Slide 11 text

! 11 UIView android.view.View compile time runtime time

Slide 12

Slide 12 text

! 12 $ Let’s create a project

Slide 13

Slide 13 text

! 13

Slide 14

Slide 14 text

! 14 Highly complex backend A Google Cloud Function which run Puppeteer every hour (with Cloud Scheduler) and store a JSON file

Slide 15

Slide 15 text

! 15 { "dates": [ "2019-05-19", "2019-05-20", // … ], "pools": [ { "id": "alfred-nakache", "name": "Alfred Nakache", "coordinates": { "latitude": 48.871687, "longitude": 2.378887 }, "hours": [ [["08:00", "18:00"]], [], [["07:00", "08:30"], ["11:30", "13:30"], ["16:30", "18:00"]] // … Backend

Slide 16

Slide 16 text

! 16 $ brew install node yarn watchman $ yarn global add react-native-cli reason-cli@latest-macos Things you should install — or —

Slide 17

Slide 17 text

! 17 $ react-native init ParisPiscine && cd ParisPiscine

Slide 18

Slide 18 text

! 18 $ yarn add --dev bs-platform $ yarn add reason-react $ yarn add git://github.com/reasonml-community/bs- react-native#756f2f34fefeed1c3d1ed389330278ecd0434aca $ yarn add bs-fetch @glennsl/bs-json # extras $ touch bsconfig.json

Slide 19

Slide 19 text

! 19 // bsconfig.json { "name": "paris-piscine", "version": "0.1.0", "suffix": ".bs.js", "refmt": 3, "warnings": { "error": "+101" }, "reason": { "react-jsx": 3 }, "bs-dependencies": [ "@glennsl/bs-json", "bs-fetch", "reason-react", "bs-react-native-monorepo/reason-react-native" ], // …

Slide 20

Slide 20 text

! 20 // …bsconfig.json "package-specs": { "module": "es6", "in-source": true }, "sources": { "dir": "src", "subdirs": true }, "js-post-build": { "cmd": "./node_modules/bs-react-native-monorepo/ git-monorepo-usage-trick" } }

Slide 21

Slide 21 text

! 21 // package.json { "name": "paris-piscine", "version": "0.0.1", "scripts": { "bs:clean": "bsb -clean-world", "bs:start": "bsb -make-world -w", "bs:build": "bsb -make-world", "rn:start": "node node_modules/react-native/ local-cli/cli.js start" }, // …

Slide 22

Slide 22 text

! 22 $ rm -rf .buckconfig .flowconfig __tests__ App.js $ mkdir src && touch src/App.re A bit of cleanup

Slide 23

Slide 23 text

! 23 // src/App.re open ReactNative; let styles = StyleSheet.create( Style.{ "container": style(~flex=1., ~alignItems=`center, ~justifyContent=`center, ()), "text": style(~fontSize=24., ()), }, );

Slide 24

Slide 24 text

! 24 // …src/App.re [@react.component] let make = () X> { "Hello LilleFP”u>React.string ; };

Slide 25

Slide 25 text

! 25 // src/App.bs.js // Generated by BUCKLESCRIPT VERSION 5.0.3 import * as React from "react"; import * as ReactNative from "react-native"; var styles = ReactNative.StyleSheet.create({ container: { alignItems: “center”, flex: 1, justifyContent: “center”, }, text: { fontSize: 24 }, });

Slide 26

Slide 26 text

! 26 // …src/App.bs.js function App(Props) { return React.createElement(ReactNative.View, { style: styles.container, children: React.createElement(ReactNative.Text, { style: styles.text, children: "Hello LilleFP", }), }); } var make = App; export { styles, make };

Slide 27

Slide 27 text

! 27 // index.js import { AppRegistry } from "react-native"; import { make as App } from "./src/App.bs.js"; import { name as appName } from "./app.json"; AppRegistry.registerComponent(appName, () X> App);

Slide 28

Slide 28 text

! 28

Slide 29

Slide 29 text

! 29

Slide 30

Slide 30 text

! 30 // Pools.re let url = "http://localhost:8080"; type coordinates = { latitude: float, longitude: float, }; type pool = {coordinates}; type fetchedData = { dates: array(string), pools: array(pool), }; Fetching & decoding data

Slide 31

Slide 31 text

! 31 // …Pools.re module Decode = { open Json.Decode; let coordinates = json X> { latitude: field("latitude", float, json), longitude: field("longitude", float, json), }; let pool = json X> {coordinates: field("coordinates", coordinates, json)}; let fetchedData = json X> { dates: field("dates", array(string), json), pools: field("pools", array(pool), json), }; }; Fetching & decoding data

Slide 32

Slide 32 text

! 32 // …Pools.re let fetchData = () X> Js.Promise.( Fetch.fetch(url) yz then_(Fetch.Response.json) yz then_(json X> Decode.fetchedData(json)u>resolve) ); Fetching & decoding data

Slide 33

Slide 33 text

! 33 // App.re open ReactNative; open Belt; type state = | Loading | Error | Loaded(Pools.fetchedData); type action = | FetchData | ReceiveError | ReceiveData(Pools.fetchedData); Displaying data

Slide 34

Slide 34 text

! 34 // …App.re let styles = StyleSheet.create( Style.{ "container": style(~flex=1.,~alignItems=`center, ~justifyContent=`center, ()), }, ); Displaying data

Slide 35

Slide 35 text

! 35 // …App.re [@react.component] let make = () X> { let (state, dispatch) = React.useReducer( (state, action) X> switch (action) { | FetchData X> Loading | ReceiveError X> Error | ReceiveData(data) X> Loaded(data) }, Loading, ); Displaying data

Slide 36

Slide 36 text

! 36 // …App.re [@react.component] let make = () X> { // … let displayFetchingError = () X> Alert.alert( ~title={js|Une erreur est survenue|js}, ~message={js|Impossible d'accéder aux données|js}, (), ); Displaying data

Slide 37

Slide 37 text

! 37 React.useEffect1(() X> { Js.Promise.( Pools.fetchData() yz then_((data: Pools.fetchedData) X> dispatch(ReceiveData(data))u>resolve ) yz catch(_error X> { displayFetchingError(); dispatch(ReceiveError)u>resolve; }) yz ignore ); None; }, [||]); Displaying data

Slide 38

Slide 38 text

! 38 {switch (state) { | Loading X> | Error X> React.null | Loaded(data) X> switch (data.pools[0]) { | Some(pool) X> {("First pool: " Z[ pool.name)u>React.string} | None X> "No pool found"u>React.string } }} Displaying data

Slide 39

Slide 39 text

! 39 All of this is cool, yeah But how do I use existing libs ?

Slide 40

Slide 40 text

! 40 $ yarn add react-navigation react-native-gesture-handler react-native-maps $ react-native link react-native-gesture-handler $ react-native link react-native-maps Let’s add some deps

Slide 41

Slide 41 text

! 41 // src/vendor/ReactNavigation.re type stackNavigatorConfig; type navigationProp('routeParams, 'pushedParams) = { . [@bs.meth] "push": (string, 'pushedParams) X> unit, [@bs.meth] "goBack": unit X> unit, "state": { . "key": string, "routeName": string, "params": 'routeParams, }, }; Binding example n°1 (react-navigation)

Slide 42

Slide 42 text

! 42 // …src/vendor/ReactNavigation.re [@bs.obj] external stackNavigatorConfig: (~headerMode: [@bs.string] [ | `float | `screen | `none]=?, unit) X> stackNavigatorConfig = ""; [@bs.module "react-navigation"] external createStackNavigator: (Js.t('a), stackNavigatorConfig) X> React.element = ""; [@bs.module "react-navigation"] external createAppContainer: React.element X> React.element = ""; Binding example n°1 (react-navigation)

Slide 43

Slide 43 text

! 43 // src/vendor/MapView.re open ReactNative; include NativeElement; type region; [@bs.obj] external region: ( ~latitude: float, ~longitude: float, ~latitudeDelta: float, ~longitudeDelta: float ) X> region = ""; Binding example n°2 (react-native-maps)

Slide 44

Slide 44 text

! 44 // …src/vendor/MapView.re [@react.component] [@bs.module "react-native-maps"] external make: ( ~ref: ref=?, ~style: Style.t=?, ~region: region=?, ~children: React.element=? ) X> React.element = "default"; Binding example n°2 (react-native-maps)

Slide 45

Slide 45 text

! 45 & Diving in the full codebase

Slide 46

Slide 46 text

! 46 Thank you ! Questions ?