Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Des apps multiplateformes avec reason-react-native

Des apps multiplateformes avec reason-react-native

Mathieu Acthernoene

May 21, 2019
Tweet

More Decks by Mathieu Acthernoene

Other Decks in Technology

Transcript

  1. ! 3 ⏱ Disclaimer 30 min is short I will

    be brief on non-primary points
  2. ! 4 React Native A mobile framework to build user

    interfaces Supported platforms : iOS, Android & UWP
  3. ! 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
  4. ! 8 type user = { name: string, age: int,

    }; let me = { name: “Matthieu", age: 27, }; let meLater = { ...me, age: 28, }; ReasonML
  5. ! 9 ReasonML let sayHi = name X> Js.log("Hi "

    Z[ name Z[ “!”); let add = (a, b) X> a +. b;
  6. ! 14 Highly complex backend A Google Cloud Function which

    run Puppeteer every hour (with Cloud Scheduler) and store a JSON file
  7. ! 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
  8. ! 16 $ brew install node yarn watchman $ yarn

    global add react-native-cli reason-cli@latest-macos Things you should install — or —
  9. ! 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
  10. ! 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" ], // …
  11. ! 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" } }
  12. ! 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" }, // …
  13. ! 22 $ rm -rf .buckconfig .flowconfig __tests__ App.js $

    mkdir src && touch src/App.re A bit of cleanup
  14. ! 23 // src/App.re open ReactNative; let styles = StyleSheet.create(

    Style.{ "container": style(~flex=1., ~alignItems=`center, ~justifyContent=`center, ()), "text": style(~fontSize=24., ()), }, );
  15. ! 24 // …src/App.re [@react.component] let make = () X>

    { <View style=styles##container> <Text style=styles##text> "Hello LilleFP”u>React.string </Text> </View>; };
  16. ! 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 }, });
  17. ! 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 };
  18. ! 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);
  19. ! 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
  20. ! 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
  21. ! 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
  22. ! 33 // App.re open ReactNative; open Belt; type state

    = | Loading | Error | Loaded(Pools.fetchedData); type action = | FetchData | ReceiveError | ReceiveData(Pools.fetchedData); Displaying data
  23. ! 34 // …App.re let styles = StyleSheet.create( Style.{ "container":

    style(~flex=1.,~alignItems=`center, ~justifyContent=`center, ()), }, ); Displaying data
  24. ! 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
  25. ! 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
  26. ! 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
  27. ! 38 <View style=styles##container> {switch (state) { | Loading X>

    <ActivityIndicator size=ActivityIndicator.Size.large /> | Error X> React.null | Loaded(data) X> switch (data.pools[0]) { | Some(pool) X> <Text> {("First pool: " Z[ pool.name)u>React.string} </Text> | None X> <Text> "No pool found"u>React.string </Text> } }} </View> Displaying data
  28. ! 39 All of this is cool, yeah But how

    do I use existing libs ?
  29. ! 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
  30. ! 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)
  31. ! 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)
  32. ! 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)
  33. ! 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)