How To Create a Music Player in Reason With The useContext Hook Part 2
Tags: reason, react, learning, tutorial
Published: 2019-10-21 12:24:16

---
title: How To Create a Music Player in Reason With The useContext Hook Part 2
published: true
tags: reasonml, react, learning, tutorial
series: How To Create a Music Player in Reason
canonical_url: https://www.rockyourcode.com/how-to-create-a-music-player-in-reason-with-the-use-context-hook-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.

---

In the last [post](https://dev.to/sophiabrandt/how-to-create-a-music-player-in-reason-with-the-usecontext-hook-part-1-59dj), we set up our project: a music player with `useContext` in ReasonReact.

You can find the **[demo on GitHub pages][demo]** and the full code on **[GitHub][repo]**.

The tutorial is a port from the React tutorial **[How to Use the useContext Hook in React][upmostly]** by James King.

## Type-Driven Development

ReasonReact is a statically typed language. We should now think about our data model and create types. That will help to flesh out our app's state.

We need a model for a `musicTrack`. We need to convert each `musicTrack` into an [HTML AudioElement](https://developer.mozilla.org/en-US/docs/Web/API/HTMLAudioElement). A music track is an mp3 file that we'll upload and bundle via [webpack](https://webpack.js.org/).

`src/SharedTypes.re`:

```reasonml
type musicTrack = {
  name: string,
  file: string,
};
```

The above code shows a [record type][record]:

> Records are like JavaScript objects but are
>
> - lighter
> - immutable by default
> - fixed in field names and types
> - very fast
> - a bit more rigidly typed

But we'll need more than one `musicTrack`, so let's create a type for a collection of tracks:

```reason
type musicTracks = array(musicTrack);
```

Now, let's think about the app state. We have a collection of tracks that we'll want to play or pause. So the state needs to communicate if a track plays, which one it is, or if no track is playing:

```reasonml
type playing =
  | Playing(int) // track is playing and also has an index of type integer
  | NotPlaying;  // no track is playing
```

Here we can see the power of ReasonML's type system. With JavaScript, you will have to keep track of `isPlaying` and the track's `index`. For example:

```js
const initialState = {
  tracks: [
    { name: 'Benjamin Tissot - Summer', file: summer },
    { name: 'Benjamin Tissot - Ukulele', file: ukulele },
    { name: 'Benjamin Tissot - Creative Minds', file: creativeminds },
  ],
  isPlaying: false,
  currentTrackIndex: null,
}
```

But that code could create a bug. Potentially we could both set `isPlaying` to `true`, but still have a `currentTrackIndex` of `null`. There should be a relationship between those two pieces, but we can't model that with React.js.  
Of course, you could use libraries (i.e., [xstate](https://xstate.js.org/docs/)).  
But ReasonML offers this functionality out of the box with [variants][variant].  
(A variant is similar to a [TypeScript enum](https://www.typescriptlang.org/docs/handbook/enums.html).)

In our case, we can now finish our data model:

```reasonml
/* src/SharedTypes.re */

type musicTrack = {
  name: string,
  file: string,
};

type musicTracks = array(musicTrack);

type playing =
  | Playing(int)
  | NotPlaying;

type state = {
  tracks: musicTracks,
  playing,
};

```

## Create a Context

Here is the `useMusicPlayerContext.js` file from the [original blog post][upmostly]:

```js
import React, { useState } from 'react'

const MusicPlayerContext = React.createContext([{}, () => {}]) // creates Context

const MusicPlayerProvider = props => {
  const [state, setState] = useState({
    tracks: [
      {
        name: 'Lost Chameleon - Genesis',
      },
      {
        name: 'The Hipsta - Shaken Soda',
      },
      {
        name: 'Tobu - Such Fun',
      },
    ],
    currentTrackIndex: null,
    isPlaying: false,
  })
  return (
    // add state to Context Provider
    <MusicPlayerContext.Provider value={[state, setState]}>
      {props.children}
    </MusicPlayerContext.Provider>
  )
}

export { MusicPlayerContext, MusicPlayerProvider }
```

As you can see, we can create a Context with an empty JavaScript object. Inside the Provider, we switch it out with a `useState` hook.

**How can we do the same with ReasonReact?**

Let's create the initial state for the app first. We already defined the type in `src/SharedTypes.re`:

```reasonml
/* src/MusicPlayer.re */

let initialState: SharedTypes.state = {
  tracks: [|
    { name: 'Benjamin Tissot - Summer', file: "summer" },
    { name: 'Benjamin Tissot - Ukulele', file: "ukulele" },
    { name: 'Benjamin Tissot - Creative Minds', file: "creativeminds" },
  |],
  isPlaying: false,
};

```

It almost looks the same. Arrays use a different syntax than JavaScript (`[||]`), and we have to tell Reason that the `initialState` binding is of the type `SharedTypes.state` (which refers to the other file we already created).  
`let` bindings are immutable, [in case you're wondering][let].

We'll manage state with `useReducer` instead of `useState`. It works better with a record.

Let's create some dummy values:

```reasonml
type action =
  | DoSomething;

let reducer = (state: SharedTypes.state, action) =>
  switch (action) {
  | DoSomething => state
  };

```

Now we can create the Context:

```reasonml
// the type of the dispatch function is action => unit
// initialize the Context with state and `ignore`

let musicPlayerContext = React.createContext((initialState, ignore));
```

Now create the Provider and the main component. We'll use the `MusicPlayer` component in other modules of our app.

```reasonml
module MusicPlayerProvider = {
  let makeProps = (~value, ~children, ()) => {
    "value": value,
    "children": children,
  };
  let make = React.Context.provider(musicPlayerContext);
};

[@react.component]
let make = (~children) => {
  let (state, dispatch) = React.useReducer(reducer, initialState);

  <MusicPlayerProvider value=(state, dispatch)>
    children
  </MusicPlayerProvider>;
};
```

Reason's way is more complex. I had to search for how [useContext works in ReasonReact][reasonhooks] and fumble my way through.  
Margarita Krutikova wrote an excellent blog post about [ReasonReact's context][contextexplained], if you're interested.

Here is the Context file in its full glory:
`src/MusicPlayer.re`

```reasonml
let initialState: SharedTypes.state = {
  tracks: [|
    { name: 'Benjamin Tissot - Summer', file: "summer" },
    { name: 'Benjamin Tissot - Ukulele', file: "ukulele" },
    { name: 'Benjamin Tissot - Creative Minds', file: "creativeminds" },
  |],
  isPlaying: false,
};

type action =
  | DoSomething;

let reducer = (state: SharedTypes.state, action) =>
  switch (action) {
  | DoSomething => state
  };

let musicPlayerContext = React.createContext((initialState, ignore));

module MusicPlayerProvider = {
  let makeProps = (~value, ~children, ()) => {
    "value": value,
    "children": children,
  };
  let make = React.Context.provider(musicPlayerContext);
};

[@react.component]
let make = (~children) => {
  let (state, dispatch) = React.useReducer(reducer, initialState);

  <MusicPlayerProvider value=(state, dispatch)>
    children
  </MusicPlayerProvider>;
};

```

We will be able to manage the app's state in this module. We'll use the `MusicProvider` to pass the state and the reducer function to other components of the app.

## Add Context to Main App

It's easy to connect the context to the rest of the app. Go to `src/App.re` and include the `MusicPlayer` module:

```reasonml
open ReactUtils;

[@react.component]
let make = () =>
  <div className="section is-fullheignt">
    <div className="container">
      <div className="column is-6 is-offset-4">
        <h1 className="is-size-2 has-text-centered">
          {s("Reason Music Player")}
        </h1>
        <br />
        <MusicPlayer /> // * new *
      </div>
    </div>
  </div>;
```

`MusicPlayer` will wrap two other components (`TrackList` and `PlayerControls`) which we'll create later. Those components will have access to the context.

## Recap

In this post, we created the context for the music player application. We used types, `useContext`, and `useReducer`.  
The syntax for ReasonReact is more complicated, but our types will minimize some bugs.

### Further Reading

- [How to Use the useContext Hook in React][upmostly] by James King
- [ReasonReact][reasonreact]
- [ReasonReact hook recipes][reasonhooks] by Paul Shen
- [ReasonReact context explained in action][contextexplained] by Margarita Krutikova
- [GitHub Repository for the music player][repo]

[demo]: https://sophiabrandt.github.io/reason-music-player/
[repo]: https://github.com/sophiabrandt/reason-music-player
[upmostly]: https://upmostly.com/tutorials/how-to-use-the-usecontext-hook-in-react
[reasonbestpractices]: https://dev.to/fhammerschmidt/reason-react-best-practices-2cb7
[reasonreact]: https://reasonml.github.io/reason-react/
[record]: https://reasonml.github.io/docs/en/record
[variant]: https://reasonml.github.io/docs/en/variant
[reasonhooks]: http://bypaulshen.com/articles/reason-react-hook-recipes
[let]: https://reasonml.github.io/docs/en/let-binding
[contextexplained]: https://dev.to/margaretkrutikova/reason-react-context-explained-in-action-5eki