In the previous part we have implemented simple rest server that returns companies and employees. This time we’ll add some frontend functionality (with some small server changes) to see how Reason can help us (or not :)) in that area. Reason comes with ReasonReact project, which of course uses React under the hood. But a few things are different.

Build process

Let’s start with modifying the build process. Last time I used bucklescript’s bsb tool to compile re files and then webpack to glue emitted js files together. That worked well until I tried to include css file inside the re file. Css file was located in src directory while webpack was looking for it in the output directory. The solution for that was to use the bs-loader plugin for webpack. Since then everything started to work as expected. Basic webpack file is really simple:

Look and feel

Let’s take a look at the final version of the application. Frontend is really simple and consists of three components: Header, CompanyList and SearchResults.

CSS comes from Kube framework.

Header

In TypeScript that component definition could look like this:

Stateless component definition in Reason is a little bit different:

Firstly, there can be only one component per module. Since every file in Reason is a module, you don’t have to declare module name inside the file. If you want to have more components in one file simply use nested modules — for example:

and then use it like this:

<Header.SimpleHeader/>

ReasonReact doesn’t use classes for components. Instead it uses records with overridable fields. That means there is no this pointer — records are immutable. Records in Reason are defined similarly to JS’ objects (but they are not objects — they are fixed in fields names and types and they are translated into JavaScript’s arrays which makes them really efficient). Let’s declare records signature:

..then record instance:

Opposite to modules type names begin with lowercase letter. Now to modify a record, the spread operator is used.

Looks familiar? Looks simple? Good :)

With that knowledge, the Header definition should not feel magical anymore. It is simply a bunch of functions and records enclosed in a module with some convention on top of it.

Let’s take a look once again at the header definition. The first part…

…creates component record with default lifecycle methods implementations (like didMount, render, shouldUpdate etc)

The second part…

…defines the make function, which is called at every JSX invocation and it returns another record with user’s overrides. Notice the spread operator …component! ReasonReact.statelessComponent cannot be inlined here to avoid component recreation at each rerender (component would lose its actual state).

Component’s props are declared as the parameters of the make function. If you don’t know — in Reason, double colon :: is a named parameter. Underscore in front of parameter’s name tells the compiler not to warn if that parameter is not used.

Header component is a stateless component and it only passes callbacks from the main app to its search input element.

One annoyance in ReasonReact (at least for now) are explicit conversions of strings to React elements. So instead of being able to write:

one has to use ReasonReact.stringToElement function or, like in my case, an alias for it named str:

CompanyList, SearchResults

CompanyList is also a stateless component. What’s interesting here are explicit type definitions of named parameters:

That is supposed to be simplified in the near future, but for now if the name of the parameter and it’s name inside a function body is the same — it must be written twice. That makes such declarations a little bit verbose.

Other than that there is nothing especially interesting in the component implementation. It simply takes company and employees lists and iterates over them to render them on the screen. `SearchResults` is also very similar.

App

Much more interesting is the App component. This is the only stateful component in the application.

In this example I used a stateful component with integrated reducer to handle all state changes. There is also statefulComponent version for “more classical” state handling.

Reducer version is based on Redux way of handling state changes. In theory this is nothing more than:

where action is a function that takes some input values (payload) and the previous state of the component (previousState) and where reducer is a function that is responsible for applying action with correct inputs and other necessary logic to return the new state. It’s a very functional way of dealing with state changes. Immutable actions and unidirectional data flow makes it easy to follow what changed where, when and why.

In ReactReason there is no need for redux as this way of handling component’s state was built in it.

But what we need in the first place is the initial state:

Then action type must be defined with a list of all component actions:

Actions can take a payload. For example when CompaniesLoaded action will be taken, it will come with company list.

To handle actions, a reducer function must be provided:

In most cases its implementation comes to pattern matching over action argument. Every action must return the new state wrapped in update variant. There are a few to choose but in most cases ReasonReact.Update, ReasonReact.NoUpdate and ReasonReact.UpdateWithSideEffects will be used. The last one allows to update the state and execute function after that but before next render. In ReactJs that would be equivalent of:

Of course the whole reducer is strongly typed. It’s not possible to misspell action name or to incorrectly interpret action’s payload. Additionally action matching is exhaustive. That means you won’t be able to compile your code until every action is handled. This is the real improvement over redux even if it is combined with typescript.

Reducer actions can be fired through reduce function available in most component lifecycle methods, for example in render. In this application I’m passing reduce to the outer function:

to updateQuery:

and to executeQuery:

executeQuery fetches data from the server and passes it to the reducer. CompanyService is a rest client that uses bs-fetch (bindings to fetch) and bs-json (library to encode/decode json in bucklescript).

And that is pretty much it :)

Summary

Reason and ReasonReact are still very fresh but they seem very usable even now. There are some unnecessary verbosity here and there (like let query = (ReactDOMRe.domElementToObj (ReactEventRe.Form.target event))##value or the need for fun keyword in every context) or strange syntax constraints (like spaces around JSX children), but the overall experience (after getting used to the new language and its concepts) is very positive. The language is powerful and quite easy to learn and has familiar syntax. That’s a big plus. ReasonReact is a little bit different than original React but for someone with ReactJs/Redux experience that should not be a big problem.

Links

1. Source of example application

2. ReasonML

3. ReasonReact

4. Todo app in Reason

5. A First Reason React app for Javascript developers