Say hello to ReasonReact - building a lyrics search web app
Tags: reason, react, functional
Published: 2020-08-07 17:11:07

👨‍💻[Source code available on github](https://github.com/carlosfrodrigues/lyrics-finder)
## Why ReasonReact?
ReasonML is a new syntax and toolchain for Ocaml created by Jordan Walke, a software engineer at Facebook, who also created React.
It give us the OCaml's typesystem with a similar javascript syntax.
With ReasonReact, we can actually write Reason code that compiles to idiomatic ReactJS.

## Creating a new project
We're going to build a lyrics search web app using the [lyrics.ovh API](https://lyricsovh.docs.apiary.io/)
![gif](https://raw.githubusercontent.com/carlosfrodrigues/lyrics-finder/master/lyrics-finder.gif)
First, with the npm installed in your machine, execute:

```sh
npm install --global bs-platform
bsb -init lyrics-finder -theme react-hooks
npm start
# in a new tab
npm run server
```
It will generate a template example code, let's delete all the components that are rendered on the `index.re`. Inside `src` create the folder LyricsFinder with the file `LyricsFinder.re`,
On `index.re` let's render only this new component :

```reasonML
ReactDOMRe.render(
  <LyricsFinder />,
  makeContainer("Lyrics Finder"),
);
```

## Working on a ReasonReact component
We will start working on the LyricsFinder.re file
So, let's declare the type lyrics in the best oCaml-like way:

```reasonML
type lyrics =
  | NotLoadedLyrics
  | ErrorFetchingLyrics
  | LoadedLyrics(string);
```
Now it's time to make the react component, all you need is use the `@react.component` decorator and create the
make function. 
Inside our component, let's use React Hooks to set the state of our application. We will define the lyrics, artistName and songName:

```reasonML
[@react.component]
let make = () => {
  let (lyrics, setLyrics) = React.useState(() => NotLoadedLyrics);
  let(artistName, setArtistName) = React.useState(() => "");
  let(songName, setSongName) = React.useState(() => "");
}
```

We must write our Reason JSX (it's a little bit different from  the regular JSX) inside the make function, so we create a form with field for the artistName and the songName and a submit button:

```reasonML
  <div>
  <form onSubmit>
  <input type_="text" id="artist" placeholder	="artist" name="artist"value=artistName onChange={
    e => {
      setArtistName(ReactEvent.Form.target(e)##value);
    }
  } />
  <input type_="text" id="song" placeholder	="song" name="song"value=songName onChange={
    e => {
      setSongName(ReactEvent.Form.target(e)##value);
    }
  } />
  <button type_="submit"> {React.string("submit")} </button>
  </form>
  </div>
```

As we can see above the `onChange` function for the inputs change the state of the `artistName` and `songName`. What we have to do now is create a function for the submit button, we will need the fetch function, Bucklescript, wich transpile code from Ocaml to Javascript, can help us giving the external fetch function from javascript:

```reasonML
[@bs.val] external fetch: string => Js.Promise.t('a) = "fetch";
```
So let's create the onSubmit function. This function returns the unit type, in Ocaml it's used to represent the type of expressions that are evaluated for a side-effect only.

```reasonML
  let onSubmit = (e: ReactEvent.Form.t): unit => {
    ReactEvent.Form.preventDefault(e);
    Js.Promise.(
      fetch("https://api.lyrics.ovh/v1/" ++ artistName ++ "/" ++ songName)
      |> then_(response => response##json())
      |> then_(jsonResponse => {
          if(jsonResponse##hasOwnProperty("lyrics")){
          setLyrics(_previousState => LoadedLyrics(jsonResponse##lyrics));
          }else{
            setLyrics(_previousState => ErrorFetchingLyrics);
          }
          Js.Promise.resolve();

      })
      |> catch(_err => {
          setLyrics(_previousState => ErrorFetchingLyrics);
           Js.Promise.resolve();
      })
      |> ignore
    );
  };
```
As we can see, with the `onSubmit` function the lyrics will receive the value according to the fetch response.
We will use the pattern matching on lyrics to give the result, on the reason JSX part: 
```reasonML
  {switch (lyrics) {
    | NotLoadedLyrics => React.string("Search for a song")
    | ErrorFetchingLyrics => React.string("Error Loading Lyrics!")
    | LoadedLyrics(lyricsText) =>
    Js.String.split("\n", lyricsText)
    ->Belt.Array.map((lyricLine) => {
      <div><label>{React.string(lyricLine)}</label></div>;
    
    })
    ->React.array
  }}
```

So we finished creating the app! Hope you enjoyed this tutorial, feel free to download the code on github:
{% github carlosfrodrigues/lyrics-finder no-readme %}

### Useful Links:
* [ReasonReact Webpage](https://reasonml.github.io/reason-react/)
* [Reason Discord Channel](https://discord.gg/reasonml)
* [Awesome ReasonML github repo](https://github.com/vramana/awesome-reasonml)