DEV Community

Reason(React) Best Practices - Part 3

Florian Hammerschmidt on October 26, 2019

Welcome back to the third part of my blog mini series (Part 1, Part 2) which derived from my talk at @reasonvienna. This part is all about writing ...
Collapse
 
idkjs profile image
Alain • • Edited

Please don't let this be all, brother. I have tried at various times to write bindings to the github.com/developit/mitt library for no other reason than its super short and should be doable as a learning experience. Why not a bonus post in this series binding that library?!! Thanks for sharing your knowledge here. This has been invaluable and a go to series for me.

Peace to you.

Collapse
 
idkjs profile image
Alain • • Edited

This is where I keep getting stuck.

I can't figure out how to bind to the star symbol and anything else really.
Js code is:

type EventHandlerMap = {
  '*'?: WildCardEventHandlerList,
  [type: string]: EventHandlerList,
};

and can be found here:github.com/developit/mitt/blob/2ab...

Collapse
 
yawaramin profile image
Yawar Amin • • Edited

Hey Alain, roughly speaking this should work:

module EventHandlerMap = {
  type t;
  type wildCardEventHandlerList;
  type eventHandlerList;

  [@bs.get] external star: t => option(wildCardEventHandlerList) = "*";
  [@bs.get_index] external get: (t, string) => option(eventHandlerList) = "";
};

let f(eventHandlerMap) = EventHandlerMap.star(eventHandlerMap);
let g(eventHandlerMap) = EventHandlerMap.get(eventHandlerMap, "foo");

It models the EventHandlerMap and its values as abstract types, you can fill in more details if you know them.

[EDIT: I made the get return an option because a dynamic key lookup may always return undefined.]

Thread Thread
 
fhammerschmidt profile image
Florian Hammerschmidt • • Edited

Yawar, you really are an OCaml/Reason guru and probably the most helpful guy I ever met in any community. Keep it up!

Thread Thread
 
yawaramin profile image
Yawar Amin •

Aw, shucks 😊it's very gratifying to see people get pulled into the ReasonML community/tech ecosystem and make cool stuff. And one of these days, I'll manage to ship some Reason at work too–fingers crossed 😁

Collapse
 
fhammerschmidt profile image
Florian Hammerschmidt •

Ahhh, that's a tough one. But the library is short enough that I would rewrite in Reason completely. Many JS hacks though (I suppose for performance).

Having a Map of different types is just not possible (IMHO) in Reason. You'd need a wrapper EventHandlerList type which works for both WildCardEventHandlerList and EventHandlerList.

Sometimes, you just need to use plain JS Objects and Obj.magic, I guess, sorry.

Collapse
 
idkjs profile image
Alain •

Question:

When I through the alertType into an editor:

type alertType = [@bs.string] [
              | `default
              | [@bs.as "plain-text"] `plainText
              | [@bs.as "secure-text"] `secureText
              | [@bs.as "login-password"] `loginPassword
            ];
Enter fullscreen mode Exit fullscreen mode

I get bucklescript warnings on bs.string and bs.as.

alertType screenshot

Why is that?

Collapse
 
fhammerschmidt profile image
Florian Hammerschmidt •

Hm, it's actually only for demonstration purposes. bs.string only works for arguments of functions. That's why it is inlined in the sources.

Collapse
 
hodatorabi profile image
hodatorabi •

Hi thanks for sharing this. I haven't actually run this code but when trying to do something similar with bs.unwrap and labelled arguments I ran into github.com/BuckleScript/bucklescri... Should I be doing something differently?

Collapse
 
fhammerschmidt profile image
Florian Hammerschmidt • • Edited

So bs.unwrap does unfortunately not work with @bs.obj. This means it also does not work with [@react.component] either, which uses @bs.obj internally, if you stumbled over that.

It works perfectly in the source code of the example which does not use @bs.obj.

If you are on BuckleScript 7.1 or higher, there is an alternative, though: [@unboxed]:

For instance if you take the callbackOrButtons from above:

type button;

module CallbackOrButtons = {
    [@unboxed] 
    type t =
        | Any('a): t;

    let makeCallback: (. string => unit) => t = (. s) => Any(s);
    let makeButtons: array(button) => t = i => Any(i);
};

/* The Obj.magic is only for demonstration purposes, 
   I did not want to type out the button type. */
let buttons = CallbackOrButtons.makeButtons([|"<Button/>"->Obj.magic|])
let callback = CallbackOrButtons.makeCallback(. a => Js.log(a))

then you would call ~callbackOrButtons=buttons or ~callbackOrButtons=callback in the binding from the article.

Collapse
 
hodatorabi profile image
hodatorabi •

Thank you, I will definitely try this.