Learning ReasonReact Step by Step Part: 2
Tags: reason, react, learning, functional
Published: 2019-09-15 10:28:18

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

**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.

---

By now we have the HTML/JSX skeleton for our input form: a simple login form styled with [Bulma][bulma].  
(The code is available on [Github](https://github.com/sophiabrandt/reason-form/commit/632547ae4a9c353beb885516d345cac02d89ccbe).)

## Stumbling Blocks With ReasonReact

The idea for this blog post series was to create a ReasonReact form with hooks to learn how ReasonML and ReasonReact work.  
I took inspiration from James King's tutorial on **[Using Custom React Hooks to Simplify Forms][upmostly]**. When I read it at the beginning of the year, it helped me to understand how the new React Hooks API works.

In the article James creates a custom `useForm` hook, so that's what I wanted to create in ReasonReact, too.

When you have HTML forms, you will need to get the [values of the HTML element (target)](https://devdocs.io/html/attributes#target-attribute), so that you can store it somewhere.  
In React, you'd use the `useState` hook or a class component and store the values as state.

You could store each value as a string, or store all values as a JavaScript object, for example.

The [aforementioned blog post][upmostly] uses a JavaScript object with computed keys:

```js
const handleChange = event => {
  event.persist()
  setValues(values => ({ ...values, [event.target.name]: event.target.value }))
}
```

**ReasonML doesn't use objects in the same way that Javascript does.**  
But we do need a data structure that can handle compound data with keys and values (a "hash map"). Of course, Reason offers something like that: the **[Record][record]**.  
Records are _immutable by default_ and typed! But they don't support computed keys, you have to know the keys beforehand.

So the approach above doesn't work with ReasonML out of the box.

**BuckleScript to the rescue!** BuckleScript does a good job of explaining what we use JavaScript objects for. And the documentation offers advice on how and what to use.

So, Records won't work, let's use a [JS.Dict][bucklescriptobject]:

```reasonml
let myMap = Js.Dict.empty();
Js.Dict.set(myMap, "Allison", 10);
```

Let's **try** to create the `useForm` hook in ReasonReact (the following code doesn't work):

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

module UseForm = {
  [@react.component]
  let make = (~callback) => {
    let valuesMap = Js.Dict.empty();
    let (values, setValues) = React.useState(() => valuesMap);  // (A)

    let handleChange = (evt) => {
      let targetName = evt:string => evt->ReactEvent.Form.target##name;    // (B)
      let targetValue = evt:string => evt->ReactEvent.Form.target##value;  // (B)
      let payload = Js.Dict.set(valuesMap,{j|$targetName|j},targetValue);  // (C)

      ReactEvent.Form.persist(evt);

      setValues(payload); // (D)
    }
  }
};
```

First, we set up an empty `Js.Dict` as the initial value for the `useState` hook (`line (A)`).  
Inside the `handleChange` function we have to tell ReasonReact that the HTML target name and the HTML target value are strings (`line (B)`).  
Then we use the `Js.Dict.set` function to add the new values to the dictionary (`line (C)`) and finally try to set those values with the `useState` function (`(D)`).

I had to use BuckleScript's [string interpolation syntax](https://bucklescript.github.io/docs/en/interop-cheatsheet#string-unicode-interpolation) to create the `Js.Dict` key (`line (C)`).

Unfortunately, that doesn't work. The compiler complains on line `line (D)`:

```reasonml
Error: This expression has type unit but an expression was expected of type Js.Dict.t(ReactEvent.Form.t => string) => Js.Dict.t(ReactEvent.Form.t => string)
```

**You could always embed raw JavaScript into Reason** to work around these issues, but it's strongly discouraged.

As a newbie I'm not sure on how to continue at the moment.

How do you merge `JS.Dict` objects? The interface looks like a [JavaScript Map](https://devdocs.io/javascript/global_objects/map), but using the "object spread syntax" doesn't work either. ReasonReact uses this syntax to update their immutable records, but it doesn't work with BuckleScript's `Js.Dict`.  
Furthermore, how can I use the `useState` hook with a `Js.Dict`?

Perhaps I'm using an **anti-pattern** here, and that's why it is so hard to achieve the JavaScript solution in ReasonReact.

I'm also not sure about the **file structure**. Reason encourages fewer files and [nested modules](https://reasonml.github.io/docs/en/module), but how does that work with custom (React) hooks?

[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#docsNav
[bucklescriptobject]: https://bucklescript.github.io/docs/en/object#hash-map-mode