Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Features
Speaker Deck
PRO
Sign in
Sign up for free
Search
Search
The missing R in React
Search
Sponsored
·
SiteGround - Reliable hosting with speed, security, and support you can count on.
→
Mateusz Zatorski
July 17, 2018
Programming
45
0
Share
The missing R in React
A brief introduction to ReasonML and Reason React
Mateusz Zatorski
July 17, 2018
More Decks by Mateusz Zatorski
See All by Mateusz Zatorski
Building mobile apps in React Native using ReasonML
knowbody
2
470
React Router 1.0
knowbody
1
99
Other Decks in Programming
See All in Programming
의존성 주입과 모듈화
fornewid
0
120
Vibe하게 만드는 Flutter GenUI App With ADK , 박제창, BWAI Incheon 2026
itsmedreamwalker
0
540
実践ハーネスエンジニアリング #MOSHTech
kajitack
7
6.1k
2026-03-27 #terminalnight 変数展開とコマンド展開でターミナル作業をスマートにする方法
masasuzu
0
310
[PHPerKaigi 2026]PHPerKaigi2025の企画CodeGolfが最高すぎて社内で内製して半年運営して得た内製と運営の知見
ikezoemakoto
0
340
存在論的プログラミング: 時間と存在を記述する
koriym
5
840
CDK Deployのための ”反響定位”
watany
1
540
条件判定に名前、つけてますか? #phperkaigi #c
77web
2
990
Radical Imagining - LIFT 2025-2027 Policy Agenda
lift1998
0
250
メッセージングを利用して時間的結合を分離しよう #phperkaigi
kajitack
3
570
Don't Prompt Harder, Structure Better
kitasuke
0
640
Offline should be the norm: building local-first apps with CRDTs & Kotlin Multiplatform
renaudmathieu
0
170
Featured
See All Featured
A brief & incomplete history of UX Design for the World Wide Web: 1989–2019
jct
1
350
The Illustrated Guide to Node.js - THAT Conference 2024
reverentgeek
1
330
Accessibility Awareness
sabderemane
0
94
10 Git Anti Patterns You Should be Aware of
lemiorhan
PRO
659
61k
How to Build an AI Search Optimization Roadmap - Criteria and Steps to Take #SEOIRL
aleyda
1
2k
技術選定の審美眼(2025年版) / Understanding the Spiral of Technologies 2025 edition
twada
PRO
118
110k
Reality Check: Gamification 10 Years Later
codingconduct
0
2.1k
The MySQL Ecosystem @ GitHub 2015
samlambert
251
13k
RailsConf & Balkan Ruby 2019: The Past, Present, and Future of Rails at GitHub
eileencodes
141
35k
The Psychology of Web Performance [Beyond Tellerrand 2023]
tammyeverts
49
3.4k
Applied NLP in the Age of Generative AI
inesmontani
PRO
4
2.2k
Building AI with AI
inesmontani
PRO
1
870
Transcript
“The missing R in React” Mateusz Zatorski Twi8er: @matzatorski ⚡
GitHub: @knowbody
react-(R)outer?
react-(R)edux?
react-(R)elay?
(R)
(R)easonML
Why OCaml?
- func<onal - established language - compiled to: bytecode, fast
na<ve or JavaScript - full-featured type system Why OCaml?
Story <me
OCaml compiler OCaml syntax OCaml seman<cs Bytecode Na<ve
js_of_ocaml OCaml syntax OCaml seman<cs Bytecode Na<ve js_of_ocaml
Bloomberg
OCaml syntax OCaml seman<cs Bytecode Na<ve Compiler backend replaced by
BuckleScript OCaml syntax OCaml seman<cs BuckleScript
Facebook
What is ReasonML?
Compiler frontend replaced by Reason Reason syntax OCaml seman<cs Bytecode
Na<ve
Combining everything together Reason syntax OCaml seman<cs BuckleScript
Where we are at today? - syntax familiar to JS
developers - full power of OCaml - clean and readable JS output
Reason CLI
Reason CLI global binaries needed by editor plugins Comes with
a few extra tools: - refmt - “Reason format” - PreQer was inspired by refmt - merlin - engine powering type hint, refactor, real-<me errors, jump to defini<ons, etc. to our editors. - REPL - called rtop, interac<vely evaluates code - re:bench - online benchmarking playground - Redex - the Reason packages registry - ocamlc, ocamlopt, ocamlrun - bare ocaml compilers
Data types
Strings “Hello world” Characters ‘a’ Integers 27 -13 Floats 27.0
-13.0 Integer addiMon 27 + 13 Float addiMon 27.0 +. 13.0 Integer division/mulMplicaMon 14 / 2 * 8 Float division/mulMplicaMon 14.0 /. 2.0 *. 8.0 Float exponenMaMon 3.0 ** 3.0 String concatenaMon “Hello ” ++ “world”
Comparison > < >= <= Boolean operaMons ! && ||
Reference, physical (deep) equality === == Immutable lists [3, 2, 1] Arrays [|1, 2, 3|] Records type person = {age: int}; {age: 18} Comments /* my comment */
Tuple type let coordinates: (float, float) = (48.0, -120.0); let
xCoordinate = getX(coordinates); let yCoordinate = getY(coordinates);
Record type access the individual fields in a record using
the . operator: type person = { name: string, age: int, }; let matt = {name: "Mateusz", age: 12}; matt.name; /* "Mateusz" */ matt.age; /* 12 */
Variant type Enumerated type type shoesColor = | Red |
Blue | Green | Black; type month = | Name(string) | Num(int); let myMonth = Name("February"); let someOtherMonth = Num(3); Union type
Variant type Recursive type type intTree = | Empty /*
atomic */ | Node(int, intTree, intTree); /* compound */
Polymorphic type type list('a) = | Nil | Cons('a, list(‘a));
type stringList = list(string); let myList = Cons("foo", Cons("bar", Cons("baz", Nil))); Singly-linked list “foo” “bar” “baz” Nil
Op<on type type option('a) = | None | Some('a); let
division = (a: int, b: int) : option(int) => if (b === 0) { None; } else { Some(a / b); }; let myLuckyNumber = division(23, 45);
Expressions
let let hardMath = { let x = 4; let
y = x - 1; x + y }; x and y are not accessible outside of hardMath scope let rec factorial = n => if (n === 0) { 1; } else { n * factorial(n - 1); };
if-else let min = (x, y) => if (x <
y) { x; } else { y; };
PaYern matching
if-else let min = (x, y) => if (x <
y) { x; } else { y; }; let min = (x, y) => switch(x < y) { | true => x | false => y };
Func<ons
Labelled arguments let add = (~x, ~y) => {/* use
x and y here */}; add(~y=5, ~x=6);
Currying let add = (x, y) => x + y;
let inc = add(1); inc(10); /* 11 */
Recursive func<ons let rec factorial = n => if (n
=== 0) { 1; } else { n * factorial(n - 1); };
Modules
Modules module Calc = { let add = (x, y)
=> x + y; }; Calc.add(4, 5);
Promises let fetchData = somePromise => somePromise |> Js.Promise.then_(value =>
{ Js.log(value); Js.Promise.resolve(value + 8); }) |> Js.Promise.catch(error => { Js.log("Error!!", error); Js.Promise.resolve(-2); });
JavaScript interop
JavaScript interop let jsCalculate: (array(int), int) => int = [%bs.raw
{| function (numbers, scaleFactor) { var result = 0; numbers.forEach(number => { result += number; }); return result * scaleFactor; } |} ]; let calculate = (numbers, scaleFactor) => jsCalculate(Array.of_list(numbers), scaleFactor); Js.log(calculate([1, 2, 3], 10)); /* -> 60 */
Component
Component let component = ReasonReact.statelessComponent("App"); let make = _children =>
{ ...component, render: _self => <div> <h1> (ReasonReact.string("Welcome!")) </h1> </div>, };
Component let component = ReasonReact.statelessComponent(“App"); let make = (~message, _children)
=> { ...component, render: _self => <div> (ReasonReact.string(message)) </div>, };
Component let component = ReasonReact.statelessComponent("App"); let make = (~fooProp, ~barProp,
_children) => { ...component, /* render and lifecycle methods go here */ };
Stateful component
type state = {isEnabled: bool}; type action = | Click;
let component = ReasonReact.reducerComponent("App"); let make = _children => { ...component, initialState: () => {isEnabled: true}, reducer: (action, state) => switch (action) { | Click => ReasonReact.Update({isEnabled: ! state.isEnabled}) }, render: self => <div> <h1 onClick=(_event => self.send(Click))> (ReasonReact.string(self.state.isEnabled ? "on" : "off")) </h1> </div>, };
type state = {isEnabled: bool}; type action = | Click;
let component = ReasonReact.reducerComponent("App"); let make = _children => { ...component, initialState: () => {isEnabled: true}, reducer: (action, state) => switch (action) { | Click => ReasonReact.Update({isEnabled: ! state.isEnabled}) }, render: self => <div> <h1 onClick=(_event => self.send(Click))> (ReasonReact.string(self.state.isEnabled ? "on" : "off")) </h1> </div>, };
type state = {isEnabled: bool}; type action = | Click;
let component = ReasonReact.reducerComponent("App"); let make = _children => { ...component, initialState: () => {isEnabled: true}, reducer: (action, state) => switch (action) { | Click => ReasonReact.Update({isEnabled: ! state.isEnabled}) }, render: self => <div> <h1 onClick=(_event => self.send(Click))> (ReasonReact.string(self.state.isEnabled ? "on" : "off")) </h1> </div>, };
type state = {isEnabled: bool}; type action = | Click;
let component = ReasonReact.reducerComponent("App"); let make = _children => { ...component, initialState: () => {isEnabled: true}, reducer: (action, state) => switch (action) { | Click => ReasonReact.Update({isEnabled: ! state.isEnabled}) }, render: self => <div> <h1 onClick=(_event => self.send(Click))> (ReasonReact.string(self.state.isEnabled ? "on" : "off")) </h1> </div>, };
type state = {isEnabled: bool}; type action = | Click;
let component = ReasonReact.reducerComponent("App"); let make = _children => { ...component, initialState: () => {isEnabled: true}, reducer: (action, state) => switch (action) { | Click => ReasonReact.Update({isEnabled: ! state.isEnabled}) }, render: self => <div> <h1 onClick=(_event => self.send(Click))> (ReasonReact.string(self.state.isEnabled ? "on" : "off")) </h1> </div>, };
type state = {isEnabled: bool}; type action = | Click;
let component = ReasonReact.reducerComponent("App"); let make = _children => { ...component, initialState: () => {isEnabled: true}, reducer: (action, state) => switch (action) { | Click => ReasonReact.Update({isEnabled: ! state.isEnabled}) }, render: self => <div> <h1 onClick=(_event => self.send(Click))> (ReasonReact.string(self.state.isEnabled ? "on" : "off")) </h1> </div>, };
type state = {isEnabled: bool}; type action = | Click;
let component = ReasonReact.reducerComponent("App"); let make = _children => { ...component, initialState: () => {isEnabled: true}, reducer: (action, state) => switch (action) { | Click => ReasonReact.Update({isEnabled: ! state.isEnabled}) }, render: self => <div> <h1 onClick=(_event => self.send(Click))> (ReasonReact.string(self.state.isEnabled ? "on" : "off")) </h1> </div>, };
type state = {isEnabled: bool}; type action = | Click;
let component = ReasonReact.reducerComponent("App"); let make = _children => { ...component, initialState: () => {isEnabled: true}, reducer: (action, state) => switch (action) { | Click => ReasonReact.Update({isEnabled: ! state.isEnabled}) }, render: self => <div> <h1 onClick=(_event => self.send(Click))> (ReasonReact.string(self.state.isEnabled ? "on" : "off")) </h1> </div>, };
Arrays
type person = { name: string, age: int, }; let
people: array(person) = [| {name: "Matt", age: 26}, {name: "Czysty", age: 6}, |]; Arrays
type person = { name: string, age: int, }; let
people: array(person) = [| {name: "Matt", age: 26}, {name: "Czysty", age: 6}, |]; let component = ReasonReact.statelessComponent("App"); let make = _children => { ...component, render: _self => <div> ( ReasonReact.array( Array.map( (person: person) => <p> (ReasonReact.string(person.name)) </p>, people, ), ) ) </div>, };
type person = { name: string, age: int, }; let
people: array(person) = [| {name: "Matt", age: 26}, {name: "Czysty", age: 6}, |]; let component = ReasonReact.statelessComponent("App"); let make = _children => { ...component, render: _self => <div> ( people |> Array.map((person: person) => <p> (ReasonReact.string(person.name)) </p> ) |> ReasonReact.array ) </div>, };
Let’s build something
Install reason-cli npm install -g
[email protected]
Install bs-pla\orm npm install
-g bs-platform Add reason plugin to your editor vscode-reasonml
reason-react bsb -init my-react-app -theme react cd my-react-app && npm
install && npm start # in another tab npm run webpack