Learning ReasonReact Step by Step Part: 3
Tags: reason, react, learning, tutorial
Published: 2019-09-16 16:47:58

---
title: Learning ReasonReact Step by Step Part: 3
published: true
description: Learning how ReasonReact works from a beginner's perspective
tags: reasonml, react, learning, tutorial
series: Learning ReasonReact Step by Step
canonical_url: https://www.rockyourcode.com/learning-reason-react-step-by-step-part-3
---

**UPDATE**:  
ReasonML + BuckleScript is now [Rescript](https://rescript-lang.org/blog/bucklescript-is-rebranding).  
As the ecosystem has changed around those tools, this blog post is not accurate anymore.

---

In my [last post I tried to create a custom hook function for React forms](https://www.rockyourcode.com/learning-reason-react-step-by-step-part-2).

That didn't work as I expected. Some [kind folks helped me out and gave me some suggestions](https://dev.to/sophiabrandt/learning-reasonreact-step-by-step-part-2-3h20/comments).

Let's pivot and try something different. Instead of creating a custom hook, I'll take a step back and add the logic to the Form component. Maybe I can decouple it later.

Using a `Js.Dict` to store data (email and password) proved to be difficult and seems to be an anti-pattern.

The code we have so far is pretty bare-bones and can be seen on [GitHub](https://github.com/sophiabrandt/reason-form/commit/632547ae4a9c353beb885516d345cac02d89ccbe).

## useReducer Hook With ReasonReact

As an alternative, I will write a `useReduce` hook and add the state as a [ReasonML Record][record].  
The good news is that records are typed. The bad news is that field names (keys) are fixed. So, I'll have to hard-code the data that I want to store.

```reasonml
/* src/Form.re */
type state = {
  email: string,
  password: string,
};
```

We set up our "storage container" type where email and password are strings.

`useReducer` almost works the same as in [React][usereducer].

Let's write the actions:

```reasonml
/* src/Form.re */
type action =
  | SetEmail(string)
  | SetPassword(string)
  | SubmitForm;
```

When someone types into the email field, we have to store the input. The `SetEmail` action/function takes a parameter with the type string.  
The same is true for the password.  
And after that, we have to handle how to submit the form values. The `SubmitForm` action doesn't take any arguments.

Now, for the `useReducer`:

```reasonml
/* src/Form.re */
//...

let reducer = (state, action) => {                   // (A)
  switch (action) {
    | SetEmail(email) => {...state, email}           // (B)
    | SetPassword(password) => {...state, password}
    | SubmitForm => {                                // (B)
      Js.log({j|Form submitted with values: $state|j});
      {email: "", password: ""};
    };
  }
};


[@react.component]
let make = () => {
  let initialState = {email: "", password: ""};    // (D)

  let (state, dispatch) = React.useReducer(reducer,initialState); // (E)
```

On line A, we create the reducer function with a switch statement on each action.  
Our state is a Record, so we can use the spread syntax to update it (that looks like JavaScript!) (see `line B`).  
`SetEmail` and `SetPassword` are almost identical.  
`SubmitForm` (`line C`) uses a JavaScript `console.log` to log out our state. Then it resets the state to empty strings.  
We have to use the strange looking syntax for [string interpolation](https://bucklescript.github.io/docs/en/interop-cheatsheet.html#string-unicode-interpolation).

Inside the Form component I create an initial state with an empty email and password string (`line D`).

In React, we use a de-structured array to initialize the `useReducer`, i.e.:

```js
const [state, dispatch] = React.useReducer(reducerFunction, initialState)
```

Reason uses a [tuple](https://reasonml.github.io/docs/en/tuple), but other than that, it looks similar to React (`line E`).

Now, we only have to hook up the dispatch function to our JSX:

```reasonml
/* src/Form.re */
//... 
  let valueFromEvent = evt: string => evt->ReactEvent.Form.target##value; // (A)

  <div className="section is-fullheight">
    <div className="container">
      <div className="column is-4 is-offset-4">
        <div className="box">
          <form
            onSubmit={
              evt => {
                ReactEvent.Form.preventDefault(evt);
                dispatch(SubmitForm);
              }
            }>
            <div className="field">
              <label className="label"> {"Email Address" |> str} </label>
              <div className="control">
                <input
                  className="input"
                  type_="email"
                  name="email"
                  value={state.email}
                  required=true
                  onChange={evt => valueFromEvent(evt)->SetEmail |> dispatch} // (B)
                />
              </div>
            </div>
            <div className="field">
              <label className="label"> {"Password" |> str} </label>
              <div className="control">
                <input
                  className="input"
                  type_="password"
                  name="password"
                  value={state.password}
                  required=true
                  onChange={
                    evt => valueFromEvent(evt)->SetPassword |> dispatch // (B)
                  }
                />
              </div>
            </div>
            <button
              type_="submit" className="button is-block is-info is-fullwidth">
              {"Login" |> str}
            </button>
          </form>
        </div>
      </div>
    </div>
  </div>;
};
```

What's going on here?

I stole line A from [Jared Forsythe's tutorial][jaredforsythe]:

> In JavaScript, we'd do evt.target.value to get the current text of the input, and this is the ReasonReact equivalent. ReasonReact's bindings don't yet have a well-typed way to get the value of an input element, so we use ReactEvent.Form.target to get the "target element of the event" as a "catch-all javascript object", and get out the value with the "JavaScript accessor syntax" ##value.

> This is sacrificing some type safety, and it would be best for ReasonReact to just provide a safe way to get the input text directly, but this is what we have for now. Notice that we've annotated the return value of valueFromEvent to be string. Without this, OCaml would make the return value 'a (because we used the catch-all JavaScript object) meaning it could unify with anything, similar to the any type in Flow.

We'll use this function to hook it up to our `onChange` function for the password and email fields (see `line B`).  
First, we take the event and extract its value, then we pipe the function to our `SetEmail` or `SetPassword` action and lastly to our dispatch.

Why `->` and `|>`?

The first one is [Pipe First][pipef]:

> `->` is a convenient operator that allows you to "flip" your code inside-out. `a(b)` becomes `b->a`. It's a piece of syntax that doesn't have any runtime cost.

The other one is [Pipe Forward/Pipe Last/Reverse-Application Operator][pipel]. It basically does the same. But some functions require you to add the thing that you pipe as the first argument, and some as the last.  
It's a bit ugly. Most JavaScript and BuckleScript interop requires pipe-first. Ocaml and Reason native code works mostly with pipe-last.

## Code Repository

The complete code is on [GitHub](https://github.com/sophiabrandt/reason-form/blob/e51d7cde835ab56eec4cd98564a59d8dc77e0811/src/Form.re).

## Thoughts

`useReducer` works well with ReasonReact and will be very familiar to a React developer.  
I like ReasonML's [pattern matching](https://reasonml.github.io/docs/en/pattern-matching) and it's a good fit for `useReducer`.

### Further Reading

- [A ReasonReact Tutorial][jaredforsythe]
- [What's the difference between -> and |> in reasonml?](https://stackoverflow.com/questions/55474593/whats-the-difference-between-and-in-reasonml)

[reasonreact]: https://reasonml.github.io/reason-react/en/
[bulma]: https://bulma.io/
[upmostly]: https://upmostly.com/tutorials/using-custom-react-hooks-simplify-forms
[record]: https://reasonml.github.io/docs/en/record
[usereducer]: https://reactjs.org/docs/hooks-reference.html#usereducer
[jaredforsythe]: https://jaredforsyth.com/posts/a-reason-react-tutorial/
[pipef]: https://reasonml.github.io/docs/en/pipe-first
[pipel]: http://reasonmlhub.com/exploring-reasonml/ch_functions.html#the-reverse-application-operator