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
Write better React (v3 ReasonReact)
Search
Sponsored
·
Ship Features Fearlessly
Turn features on and off without deploys. Used by thousands of Ruby developers.
→
David Kopal
May 11, 2019
Technology
660
0
Share
Embed
Copy iframe code
Copy JS code
Copy link
Start on current slide
Write better React (v3 ReasonReact)
David Kopal
May 11, 2019
More Decks by David Kopal
See All by David Kopal
Use ReasonML in your React applications (RuhrJS 2018)
codinglawyer
2
310
Use Reason in your React applications
codinglawyer
3
600
Unleash the power of the higher-order components
codinglawyer
2
280
Other Decks in Technology
See All in Technology
気軽に使える"情報のハブ"としてのNotion活用 〜フロー情報の集積点 と、 Claude Code × Notion AI〜
syucream
1
180
Oracle Cloud Infrastructure:2026年6月度サービス・アップデート
oracle4engineer
PRO
0
290
Multi-Agent並列開発を 安全に回すための技術 / Technology for Safely Multi-Agent Parallel Development
tooppoo
0
160
不要なレビューをAIにまかせて AIコーディングの環境改善を加速した
shoota
1
260
本当の”仕事”を手放せる未来が見えた
mu7889yoon
0
120
千葉での単身赴任からAWSをやり続け、千葉に戻ってきた話
yama3133
1
120
アラート調査向けAIエージェントの本番導入とその後/AI Agents for Alert Investigation: Production Deployment and After
taddy_919
0
120
事業会社における 機械学習・推薦システム技術の活用事例と必要な能力 / ml-recsys-in-layerx-wantedly-2026
yuya4
0
160
アジャイルな経理と Claude Code と経営の未来
kawaguti
PRO
3
190
フルAIで個人開発して学んだあれこれ / yuruai vol.1
isaoshimizu
0
110
秘密度ラベル初心者が第1歩でつまづかないための「設計・運用」ポイント
seafay
PRO
1
480
【FinOps】データドリブンな意思決定を目指して
z63d
0
320
Featured
See All Featured
Bootstrapping a Software Product
garrettdimon
PRO
307
120k
Navigating the moral maze — ethical principles for Al-driven product design
skipperchong
2
400
Evolving SEO for Evolving Search Engines
ryanjones
0
220
Side Projects
sachag
455
43k
Discover your Explorer Soul
emna__ayadi
2
1.1k
Building Adaptive Systems
keathley
44
3.1k
Producing Creativity
orderedlist
PRO
348
40k
Build your cross-platform service in a week with App Engine
jlugia
234
18k
Automating Front-end Workflow
addyosmani
1370
210k
Agile Actions for Facilitating Distributed Teams - ADO2019
mkilby
0
210
brightonSEO & MeasureFest 2025 - Christian Goodrich - Winning strategies for Black Friday CRO & PPC
cargoodrich
3
740
How to Build an AI Search Optimization Roadmap - Criteria and Steps to Take #SEOIRL
aleyda
1
2.1k
Transcript
Write better React with ReasonML David Kopal
@coding_lawyer codinglawyer.io learnReasonML.com I’m David Kopal
@coding_lawyer Who heard about ason?
@coding_lawyer
@coding_lawyer Who wants to write better React?
@coding_lawyer React isn’t a native JavaScript library
@coding_lawyer
@coding_lawyer Immutability
@coding_lawyer Immutability
@coding_lawyer Immutability
@coding_lawyer Immutability Functional programming
@coding_lawyer Immutability Functional programming
@coding_lawyer Immutability Functional programming
@coding_lawyer Immutability Type system Functional programming
@coding_lawyer Immutability Type system Functional programming
@coding_lawyer Immutability Type system Functional programming
@coding_lawyer Immutability Type system Functional programming
@coding_lawyer Immutability Type system Functional programming
@coding_lawyer Immutability Type system Functional programming
@coding_lawyer Immutability Type system Functional programming
@coding_lawyer
@coding_lawyer semantics Javascript-like syntax
@coding_lawyer let fizzbuzz = (i) => switch (i mod 3,
i mod 5) { | (0, 0) => "FizzBuzz" | (0, _) => "Fizz" | (_, 0) => "Buzz" | _ => string_of_int(i) }; for (i in 1 to 100) { Js.log(fizzbuzz(i)) };
@coding_lawyer compiler
@coding_lawyer function fizzbuzz(i) { var match = i % 3
var match$1 = i % 5 if (match !== 0) { if (match$1 !== 0) { return String(i) } else { return 'Buzz' } } else if (match$1 !== 0) { return 'Fizz' } else { return 'FizzBuzz' } } for (var i = 1; i <= 100; ++i) { console.log(fizzbuzz(i)) } let fizzbuzz = (i) => switch (i mod 3, i mod 5) { | (0, 0) => "FizzBuzz" | (0, _) => "Fizz" | (_, 0) => "Buzz" | _ => string_of_int(i) }; for (i in 1 to 100) { Js.log(fizzbuzz(i)) };
@coding_lawyer JavaScript interop
@coding_lawyer
@coding_lawyer Reason is compatible with React
@coding_lawyer Reason is compatible with React React was developed for
Reason
@coding_lawyer adjusted JavaScript to React’s needs
@coding_lawyer
@coding_lawyer
@coding_lawyer
@coding_lawyer
@coding_lawyer
@coding_lawyer
@coding_lawyer
@coding_lawyer
@coding_lawyer semantics Javascript-like syntax
@coding_lawyer safer React
@coding_lawyer “[Reason] is the best way to take React to
the next level” Jordan Walke, creator of Reason, React
@coding_lawyer Tic Tac Toe
@coding_lawyer
@coding_lawyer
@coding_lawyer
@coding_lawyer
@coding_lawyer Game Board App BoardRow Square
@coding_lawyer Game Board App BoardRow Square
@coding_lawyer Game Board App BoardRow Square
@coding_lawyer Game Board App BoardRow Square
@coding_lawyer Game Board App BoardRow Square
@coding_lawyer Game Board App BoardRow Square
@coding_lawyer components
@coding_lawyer stateless component
@coding_lawyer Game Board App BoardRow Square
@coding_lawyer [@react.component] let make = (~title) !=> <div> <div className=“title">
{React.string(title)} !</div> <Game !/> !</div>;
@coding_lawyer [@react.component] let make = (~title) !=> <div> <div className=“title">
{React.string(title)} !</div> <Game !/> !</div>;
@coding_lawyer [@react.component] let make = (~title) !=> <div> <div className=“title">
{React.string(title)} !</div> <Game !/> !</div>;
@coding_lawyer [@react.component] let make = (~title) !=> <div> <div className=“title">
{React.string(title)} !</div> <Game !/> !</div>;
@coding_lawyer stateful component
@coding_lawyer Game Board App BoardRow Square
@coding_lawyer [@react.component] let make = () !=> { let (state,
dispatch) = React.useReducer( (state: state, action: action) !=> switch (action) { | Restart !=> … | ClickSquare((id: string)) !=> … }, { board: […], gameState: Playing(Cross) }, ); <div className="game"> <Board state onRestart={_evt !=> dispatch(Restart)} onMark={id !=> dispatch(ClickSquare(id))} !/> !</div>;};
@coding_lawyer [@react.component] let make = () !=> { let (state,
dispatch) = React.useReducer( (state: state, action: action) !=> switch (action) { | Restart !=> … | ClickSquare((id: string)) !=> … }, { board: […], gameState: Playing(Cross) }, ); <div className="game"> <Board state onRestart={_evt !=> dispatch(Restart)} onMark={id !=> dispatch(ClickSquare(id))} !/> !</div>;};
@coding_lawyer [@react.component] let make = () !=> { let (state,
dispatch) = React.useReducer( (state: state, action: action) !=> switch (action) { | Restart !=> … | ClickSquare((id: string)) !=> … }, { board: […], gameState: Playing(Cross) }, ); <div className="game"> <Board state onRestart={_evt !=> dispatch(Restart)} onMark={id !=> dispatch(ClickSquare(id))} !/> !</div>;};
@coding_lawyer [@react.component] let make = () !=> { let (state,
dispatch) = React.useReducer( (state: state, action: action) !=> switch (action) { | Restart !=> … | ClickSquare((id: string)) !=> … }, { board: […], gameState: Playing(Cross) }, ); <div className="game"> <Board state onRestart={_evt !=> dispatch(Restart)} onMark={id !=> dispatch(ClickSquare(id))} !/> !</div>;};
@coding_lawyer [@react.component] let make = () !=> { let (state,
dispatch) = React.useReducer( (state: state, action: action) !=> switch (action) { | Restart !=> … | ClickSquare((id: string)) !=> … }, { board: […], gameState: Playing(Cross) }, ); <div className="game"> <Board state onRestart={_evt !=> dispatch(Restart)} onMark={id !=> dispatch(ClickSquare(id))} !/> !</div>;};
@coding_lawyer [@react.component] let make = () !=> { let (state,
dispatch) = React.useReducer( (state: state, action: action) !=> switch (action) { | Restart !=> … | ClickSquare((id: string)) !=> … }, { board: […], gameState: Playing(Cross) }, ); <div className="game"> <Board state onRestart={_evt !=> dispatch(Restart)} onMark={id !=> dispatch(ClickSquare(id))} !/> !</div>;};
@coding_lawyer [@react.component] let make = () !=> { let (state,
dispatch) = React.useReducer( (state: state, action: action) !=> switch (action) { | Restart !=> … | ClickSquare((id: string)) !=> … }, { board: […], gameState: Playing(Cross) }, ); <div className="game"> <Board state onRestart={_evt !=> dispatch(Restart)} onMark={id !=> dispatch(ClickSquare(id))} !/> !</div>;};
@coding_lawyer [@react.component] let make = () !=> { let (state,
dispatch) = React.useReducer( (state: state, action: action) !=> switch (action) { | Restart !=> … | ClickSquare((id: string)) !=> … }, { board: […], gameState: Playing(Cross) }, ); <div className="game"> <Board state onRestart={_evt !=> dispatch(Restart)} onMark={id !=> dispatch(ClickSquare(id))} !/> !</div>;};
@coding_lawyer Game Board App BoardRow Square
@coding_lawyer Game Board App BoardRow Square
@coding_lawyer Game Board App BoardRow Square
@coding_lawyer Game Board App BoardRow Square
@coding_lawyer Game Board App BoardRow Square
@coding_lawyer Game Board App BoardRow Square
@coding_lawyer Game Board App BoardRow Square
@coding_lawyer [@react.component] let make = () !=> { let (state,
dispatch) = React.useReducer( (state: state, action: action) !=> switch (action) { | Restart !=> … | ClickSquare((id: string)) !=> … }, { board: […], gameState: Playing(Cross) }, ); <div className="game"> <Board state onRestart={_evt !=> dispatch(Restart)} onMark={id !=> dispatch(ClickSquare(id))} !/> !</div>;};
@coding_lawyer [@react.component] let make = () !=> { let (state,
dispatch) = React.useReducer( (state: state, action: action) !=> switch (action) { | Restart !=> … | ClickSquare((id: string)) !=> … }, { board: […], gameState: Playing(Cross) }, ); <div className="game"> <Board state onRestart={_evt !=> dispatch(Restart)} onMark={id !=> dispatch(ClickSquare(id))} !/> !</div>;};
@coding_lawyer React.useReducer( (state: state, action: action) !=> switch (action) {
| Restart !=> initialState | ClickSquare((id: string)) !=> let updatedBoard = updateBoard( state.board, state.gameState, id); let updatedGs = checkGameState3x3( updatedBoard, state.board, state.gameState); { board: updatedBoard, gameState: updatedGs, };}, …);
@coding_lawyer React.useReducer( (state: state, action: action) !=> switch (action) {
| Restart !=> initialState | ClickSquare((id: string)) !=> let updatedBoard = updateBoard( state.board, state.gameState, id); let updatedGs = checkGameState3x3( updatedBoard, state.board, state.gameState); { board: updatedBoard, gameState: updatedGs, };}, …);
@coding_lawyer [@react.component] let make = () !=> { let (state,
dispatch) = React.useReducer( (state: state, action: action) !=> switch (action) { | Restart !=> … | ClickSquare((id: string)) !=> … }, { board: […], gameState: Playing(Cross) }, ); <div className="game"> <Board state onRestart={_evt !=> dispatch(Restart)} onMark={id !=> dispatch(ClickSquare(id))} !/> !</div>;};
@coding_lawyer old syntax (v2) vs new syntax (v3)
@coding_lawyer [@react.component] let make = (~title) !=> <div> <div className=“title">
{React.string(title)} !</div> <Game !/> !</div>; new syntax (v3)
@coding_lawyer let component = ReasonReact.statelessComponent("App"); let make = (~title, _children)
=> { ...component, render: _self => <div> <div className=“title"> (ReasonReact.string(title)) </div> <Game /> </div>, }; old syntax (v2)
@coding_lawyer let component = ReasonReact.statelessComponent("App"); let make = (~title, _children)
=> { ...component, render: _self => <div> <div className=“title"> (ReasonReact.string(title)) </div> <Game /> </div>, }; old syntax (v2) var component = ReasonReact.statelessComponent("App"); function make(title, _children) { return !/* record !*/[ !/* debugName !*/component[!/* debugName !*/0], !/* reactClassInternal !*/component[!/* reactClassInternal !*/1] !/* handedOffState !*/component[!/* handedOffState !*/2], !/* willReceiveProps !*/component[!/* willReceiveProps !*/3], !/* didMount !*/component[!/* didMount !*/4], !/* didUpdate !*/component[!/* didUpdate !*/5], !/* willUnmount !*/component[!/* willUnmount !*/6], !/* willUpdate !*/component[!/* willUpdate !*/7], !/* shouldUpdate !*/component[!/* shouldUpdate !*/8], !/* render !*/(function (_self) { return React.createElement( “div", undefined, React.createElement( "div", { className: “title" }, title), React.createElement(Game.make, { }) ); }), !/* initialState !*/component[!/* initialState !*/10], !/* retainedProps !*/component[!/* retainedProps !*/11], !/* reducer !*/component[!/* reducer !*/12], !/* jsElementWrapped !*/component[!/* jsElementWrapped !*/13] ]; } exports.component = component; exports.make = make;
@coding_lawyer function App(Props) { var title = Props.title; return React.createElement(
“div", undefined, React.createElement( "div", { className: “title" }, title), React.createElement(Game.make, { }) ); } var make = App; exports.make = make; new syntax (v3) [@react.component] let make = (~title) !=> <div> <div className=“title"> {React.string(title)} !</div> <Game !/> !</div>;
@coding_lawyer const App = (Props) !=> ( <div> <div className="title">
{React.string(Props.title)} !</div> <Game !/> !</div>; ) var make = App; exports.make = make; new syntax (v3) [@react.component] let make = (~title) !=> <div> <div className=“title"> {React.string(title)} !</div> <Game !/> !</div>;
@coding_lawyer var component = ReasonReact.statelessComponent("App"); function make(title, _children) { return
!/* record !*/[ !/* debugName !*/component[!/* debugName !*/0], !/* reactClassInternal !*/component[!/* reactClassInternal !*/1], !/* handedOffState !*/component[!/* handedOffState !*/2], !/* willReceiveProps !*/component[!/* willReceiveProps !*/3], !/* didMount !*/component[!/* didMount !*/4], !/* didUpdate !*/component[!/* didUpdate !*/5], !/* willUnmount !*/component[!/* willUnmount !*/6], !/* willUpdate !*/component[!/* willUpdate !*/7], !/* shouldUpdate !*/component[!/* shouldUpdate !*/8], !/* render !*/(function (_self) { return React.createElement( “div", undefined, React.createElement( "div", { className: “title" }, title), React.createElement(Game.make, { }) ); }), !/* initialState !*/component[!/* initialState !*/10], !/* retainedProps !*/component[!/* retainedProps !*/11], !/* reducer !*/component[!/* reducer !*/12], !/* jsElementWrapped !*/component[!/* jsElementWrapped !*/13] ]; } exports.component = component; exports.make = make; const App = (Props) !=> ( <div> <div className="title"> {React.string(Props.title)} !</div> <Game !/> !</div>; ) var make = App; exports.make = make; new syntax (v3) old syntax (v2)
@coding_lawyer type system
@coding_lawyer clearer and safer code
@coding_lawyer superior to Flow and TypeScript
@coding_lawyer type inference
@coding_lawyer !/* type inference by the compiler !*/ let plus
= (a, b) !=> a + b
@coding_lawyer !/* type inference by the compiler !*/ let plus
= (a, b) !=> a + b !/* (int, int) !=> int !*/
@coding_lawyer type field = | Empty | Marked(player); type gameState
= | Playing(player) | Winner(player) | Draw; type player = | Cross | Circle;
@coding_lawyer type field = | Empty | Marked(player); type gameState
= | Playing(player) | Winner(player) | Draw; type player = | Cross | Circle;
@coding_lawyer type field = | Empty | Marked(player); type gameState
= | Playing(player) | Winner(player) | Draw; type player = | Cross | Circle;
@coding_lawyer type field = | Empty | Marked(player); type gameState
= | Playing(player) | Winner(player) | Draw; type player = | Cross | Circle;
@coding_lawyer type field = | Empty | Marked(player); type gameState
= | Playing(player) | Winner(player) | Draw; type player = | Cross | Circle;
@coding_lawyer
@coding_lawyer pattern matching
@coding_lawyer Game Board App BoardRow Square
@coding_lawyer React.useReducer( (state: state, action: action) !=> switch (action) {
| Restart !=> initialState | ClickSquare((id: string)) !=> let updatedBoard = updateBoard( state.board, state.gameState, id); let updatedGs = checkGameState3x3( updatedBoard, state.board, state.gameState); { board: updatedBoard, gameState: updatedGs, };}, …);
@coding_lawyer let updateBoard = (board: board, gameState: gameState, id) =>
board |> List.mapi((ind: int, row: row) => row |> List.mapi((index: int, value: field) => string_of_int(ind) ++ string_of_int(index) === id ? switch (gameState: gameState, value: field) { | (Playing(_), Marked(_)) => value | (Playing(player), Empty) => Marked(player) | (_, _) => Empty } : value ) );
@coding_lawyer let updateBoard = (board: board, gameState: gameState, id) =>
board |> List.mapi((ind: int, row: row) => row |> List.mapi((index: int, value: field) => string_of_int(ind) ++ string_of_int(index) === id ? switch (gameState: gameState, value: field) { | (Playing(_), Marked(_)) => value | (Playing(player), Empty) => Marked(player) | (_, _) => Empty } : value ) );
@coding_lawyer type field = | Empty | Marked(player); type gameState
= | Playing(player) | Winner(player) | Draw;
@coding_lawyer /* determines the value of the clicked square */
switch (gameState: gameState, value: field) { | (Playing(_), Marked(_)) => value | (Playing(player), Empty) => Marked(player) | (_, _) => Empty }
@coding_lawyer /* determines the value of the clicked square */
switch (gameState: gameState, value: field) { | (Playing(_), Marked(_)) => value | (Playing(player), Empty) => Marked(player) | (_, _) => Empty }
@coding_lawyer /* determines the value of the clicked square */
switch (gameState: gameState, value: field) { | (Playing(_), Marked(_)) => value | (Playing(player), Empty) => Marked(player) | (_, _) => Empty }
@coding_lawyer /* determines the value of the clicked square */
switch (gameState: gameState, value: field) { | (Playing(_), Marked(_)) => value | (Playing(player), Empty) => Marked(player) | (_, _) => Empty }
@coding_lawyer /* determines the value of the clicked square */
switch (gameState: gameState, value: field) { | (Playing(_), Marked(_)) => value | (Playing(player), Empty) => Marked(player) | (_, _) => Empty }
@coding_lawyer /* determines the value of the clicked square */
switch (gameState: gameState, value: field) { | (Playing(_), Marked(_)) => value | (Playing(player), Empty) => Marked(player) | (_, _) => Empty }
@coding_lawyer /* determines the value of the clicked square */
switch (gameState: gameState, value: field) { | (Playing(_), Marked(_)) => value | (Playing(player), Empty) => Marked(player) | (_, _) => Empty }
@coding_lawyer /* determines the value of the clicked square */
switch (gameState: gameState, value: field) { | (Playing(_), Marked(_)) => value | (Playing(player), Empty) => Marked(player) /* | (_, _) => Empty */ }
@coding_lawyer
@coding_lawyer switch ( getWinner(flattenBoard, head), gameEnded(flattenBoard), tail, ) { |
(Cross, _, _) => Winner(Cross) | (Circle, _, _) => Winner(Circle) | (_, true, []) => Draw | (_, false, []) => whosPlaying(gameState) | _ => check(tail) };
@coding_lawyer switch (match$1) { case 0 : return /* Winner
*/Block.__(1, [/* Cross */0]); case 1 : return /* Winner */Block.__(1, [/* Circle */1]); case 2 : if (match$2) { if (tail) { _rest = tail; continue ; } else { return /* Draw */0; } } else if (tail) { _rest = tail; continue ; } else { return whosPlaying(gameState); } }
@coding_lawyer Why should you use React in Reason?
@coding_lawyer Reason is compatible with React's principles
@coding_lawyer strong type system
@coding_lawyer pattern matching
@coding_lawyer functional programming features
@coding_lawyer JavaScript-like syntax React friendly
@coding_lawyer Learn ReasonML! learnReasonML.com
@coding_lawyer codinglawyer.io @coding_lawyer
@coding_lawyer Thank You
@coding_lawyer codinglawyer.io @coding_lawyer learnReasonML.com