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
 
fhammerschmidt profile image
Florian Hammerschmidt

As said, there will be more - just not under the "Reason(React) Best Practices" umbrella. I want to cover all of the bs.* annotations at some point.

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.