Margarita
Krutikova
Kyrylo
Yakymenko
Agenda
- What is Reason?
- Why should we care?
- Language features
- The new ReasonReact hooks API
- Summary
a functional programming language
React bindings for ReasonML
compiles ReasonML to JavaScript
syntactic extension of
Allows writing React applications in ReasonML
"Babel" for ReasonML
Why?
-
Statically typed, functional and pragmatic
best of two worlds
Constraints
Flexibility
Guarantees
"Sweet Spot of Pragmatism"
Why?
-
Statically typed, functional and pragmatic
best of two worlds - Powered by Facebook (used in production)
Why?
-
Statically typed, functional and pragmatic
best of two worlds - Powered by Facebook
- Fun fact: Jordan Walke, the creator of React is now working on Reason
Why?
-
Statically typed, functional and pragmatic
best of two worlds - Powered by Facebook
- Fun fact: Jordan Walke, the creator of React is now working on Reason
- Expressive type system, strong type inference
- Extremely fast builds
Why?
-
Statically typed, functional and pragmatic
best of two worlds - Powered by Facebook
- Fun fact: Jordan Walke, the creator of React is now working on Reason
- Expressive type system, strong type inference
- Extremely fast builds
- Access to the whole JS ecosystem (npm)
- Incremental adoption possible
Syntax
- reminds of Javascript
let multiply = (a, b) => a * b;
let welcome = name => "Welcome, " ++ name;
let veggies = ["pumpkin", "kale", "broccoli"];
All JavaScript
developers in the world
JavaScript developers willing to learn the OCaml syntax
The cool stuff
-
Variants + pattern matching
type response =
| Fetching
| Error(string)
| Success(user);
Variant
switch (response) {
| Fetching => <Spinner />
| Error(err) => <Error err />
| Success(user) => <MyPages user />
}
type Response = {
error: string | null,
user: User | null,
isLoading: boolean
}
if (response.loading) {
...
}
if (response.error) {
...
}
if (response.user) {
...
}
Typescript
Pattern Matching (JS)
switch (action.type) {
case "FETCHING":
return "Fetching...";
case "ERROR":
if (action.errorCode === 401) {
return "Unauthorized!";
}
if (action.errorCode === 404) {
return "Not found!";
}
return "Unknown error...";
case "SUCCESS":
return action.data;
}
Pattern Matching (Reason)
switch (action) {
| Fetching => "Fetching..."
| Error(401) => "Unauthorized!"
| Error(404) => "Not found!"
| Error(_) => "Unknown error..."
| Success(data) => data
}
Pattern Matching (routing)
if (isLoggedIn) {
if (isLoggedInAsAdmin) {
return "Access denied to admin content";
} else {
if (subRoute.length === 2 && subRoute[0] === "results") {
const playerName = subRoute[1];
return `Results for ${playerName}`;
} else if (subRoute.length === 1 && subRoute[0] === "settings") {
return "Settings";
}
}
} else {
return "Log In: ...";
}
Pattern Matching (routing)
if (!isLoggedIn) {
return "Log In: ...";
}
if (isLoggedIn && !isLoggedInAsAdmin) {
return "Access denied to admin content";
}
if (subRoute.length === 2 && subRoute[0] === "results") {
const playerName = subRoute[1];
return `Results for ${playerName}`;
}
if (subRoute.length === 1 && subRoute[0] === "settings") {
return "Settings";
}
Pattern Matching (routing)
switch (isLoggedIn, isLoggedInAsAdmin, subRoute) {
| (true, true, ["results", playerName]) =>
"Results for " ++ playerName
| (true, true, ["settings"])
| (true, true, _) => "Settings"
| (true, false, _) => "Access denied to admin content"
| (false, _, _) => "Log in: ..."
};
The cool stuff
-
Variants + pattern matching
-
Option
Option
type option('a) = None | Some('a)
-
No null / undefined
-
Safe alternative to concept of nonexistent value
let maybeInt = Some(45);
let maybeUser = Some({ name: "John" });
The cool stuff
-
Variants + pattern matching
-
Option
-
Records
Records
-
Fixed fields
-
Immutable
-
Very fast
-
Compile to arrays 🤯
Similar to JS objects, but
NOTE: Not since BuckleScript 7
This simplifies interop with JS/TS code
The cool stuff
-
Variants + pattern matching
-
Option
-
Records
-
Module system
-
Built-in formater
Automatic code formatting
JS/TS: Prettier
- prettier-vscode
- prettier-eslint
- eslint-plugin-prettier
- eslint-config-prettier
- (tslint-config-prettier)
- .prettierrc
- .eslintrc
- settings.json -> prettier settings
reason-language-server
+
refmt
ReasonML
ReasonReact
- Reason bindings for React
- Allows writing React components to whole React applications in ReasonML
- Supports React hooks as of v0.7.0!
- Future of React? 🤔
WE WANT CODE!
Start export
Started
Failed 😭
Poll for progress
Network error
Earchive export job
1
2
3
No connection
Running
Success! 🎉
State
type pollingStatus =
| NotStarted
| Loading(failedRequestsCount)
| Success(apiResponse)
| NetworkError(failedRequestsCount)
| FatalError;
type action =
| PollRequest
| PollSuccess(jobStatus)
| PollError;
State machine
let reducer = (state, action) =>
switch (action) {
| PollRequest =>
switch (state) => {
| NetworkError(failures)
| Loading(failures) => Loading(failures)
| other => Loading(0)
}
| PollError =>
switch (state) {
| Loading(failures) when failures > 3 => FatalError
| Loading(failures) => NetworkError(failures + 1)
| other => other
};
| PollSuccess(_) => ...
}
Typescript 🤔
type state = {
isStarted: boolean
isLoading: boolean
failedRequests: number
isError: boolean
isFatalError: boolean
}
if (isError && !isFatalError && failedRequests < 3) {
...
...
}
😱😭
Typescript 🤔
type state =
| { kind: "notStarted" }
| { kind: "loading", failedRequests: number }
| { kind: "networkError", failedRequests: number }
| { kind: "fatalError" }
| { kind: "success", apiResponse: jobStatus }
type pollingStatus =
| NotStarted
| Loading(failedRequestsCount)
| Success(apiResponse)
| NetworkError(failedRequestsCount)
| FatalError;
Making changes
Add "reconnect" button
type action =
| PollRequest
| PollSuccess
| PollError
| PollReset;
Restart polling timer on state change in React
switch (pollingState) {
| FatalError =>
<Text value="Internet not responding" />
<Button text="Reconnect" onClick=startTimer />
| Loading => <Spinner />
| NetworkError(_) => <Text value="Reconnecting..." />
}
* 3 hours before code freeze 😨
Build times
Clean build < 3 mins
Incremental build ~ 1 s
Antura
819 .ts files, 45,800 LOC
Clean build ~ 20 s
Incremental build ~ 5-6 s
- Variants, powerful pattern matching
- Functional language, but pragmatic
(has mutation, side effects) - Expressive, proven type system with strong type inference
- Compilation speed
- Interop with other libs - use anything on npm!
- Possibility to compile to native code
- Driven by Facebook
Summary
- Hasn't reached a critical mass yet
- Still "raw"
-
Tooling not on par with JS/TS
-
Sometimes tricky to find up-to-date libraries/bindings
Disadvantages
Adoption Strategies
No need to go all in from the start
- Start with individual components
- Start with logic/functions
- Start with API decoders
Frontend Toolkit
React + TypeScript
Elm
WebForms
React + JavaScript
Vanilla JS/TS
ReasonReact
Frontend Toolkit
React + TypeScript
Official stack
Frontend Toolkit
React + TypeScript
React + ReasonML
Free choice when starting a new project/component
Official stack
Evaluation
Frontend Toolkit
WebForms
React + JavaScript
Vanilla JS/TS
Legacy
Razor/Blazor
(99% of AP)
(Gantt 2.0)
(Header, Kanban)
Frontend Toolkit
Elm
- Existing code is (almost) bug-free
- Existing code is easy to maintain
- No reason to rewrite in React
-
Prerequisites for starting a new project:
- A good reason
(e.g. rendering performance) - Interop with existing code
(e.g. via react-elm-components)
- A good reason
Companies hiring React JavaScript TypeScript developers
Companies hiring ReasonReact, Elm, etc. developers
React JS/TS developers
ReasonReact developers
Companies hiring React JavaScript TypeScript developers
Companies hiring ReasonReact, Elm, etc. developers
module Panel = {
[@bs.module "./Panel"] [@react.component]
external make:
(
~scrollable: bool=?,
~className: string=?,
~style: ReactDOMRe.Style.t=?,
~children: React.element
) =>
React.element =
"Panel";
};
module Content = {
[@bs.module "./Content"] [@react.component]
external make:
(
~scrollable: bool=?,
~disableScroll: bool=?,
~style: ReactDOMRe.Style.t=?,
~className: string=?,
~children: React.element
) =>
React.element =
"Content";
};
ReasonReact (condensed)
By Kyrylo Yakymenko
ReasonReact (condensed)
- 386