How do I initialize an instance asynchronously in Reason?


#1

I’m working on a google extension which is a dictionary lookup app.

And the raw data(let’s say it’s a raw json) has to be requested online.
The example code is below.

let dict: Js.Dict.t(string) = Js.Dict.empty();

let lookup = (key: string): string => {
	Js.Dict.get(dict, key)
};

Api.getDictionary() /* type is Js.Promise.t(string) */
	|> Js.Promise.then_(tranferToJsDict) /* type is Js.Promise.t(Js.Dict.t(string)) */

I fetch the data somewhere and decode to data structure I prefer.
There is another function named lookup which retrieves the key(parameter)
And it would use dict for looking up.
However, I have no idea how to initialize dict via api promise since we can not mutate the global variable in reasonml.

I guess I need another pattern for this usage.
Any thought?


#2

Mutation is possible via ref https://reasonml.github.io/docs/en/mutation

let dict: ref(Js.Dict.t(string)) = ref(Js.Dict.empty());

let transferToJsDict = (result) => {
  dict :=result;
  Js.Promise.resolve(result);
}

If you’re using React, then you can also store and update it in a state variable with React.useState.


#3

The ref solution with Js.Dict.t is probably the easiest to begin with, but you can also use a mutable Hashmap if there are frequent updates: https://bucklescript.github.io/bucklescript/api/Belt_HashMap.html. If your values are strings, you can directly use HashMapString. https://bucklescript.github.io/bucklescript/api/Belt_HashMapString.html

If you want to put values into window and access them in a type-safe manner, then this approach can be useful:

module JsGlobals = {
  type t;
  [@bs.val] external t: t = "window";
  [@bs.set]
  external setDict: (t, MyDict.t) => unit = "myDict"
  [@bs.get]
  external getDict: t => MyDict.t = "myDict";
};

let g = JsGlobals.t;
JsGlobals.setDict(g, MyDict.make());
Js.log(JsGlobals.getDict(g));

This will result in the following JS:

var g = window;
// (say MyDict.make() returns a string)
g.myDict = "";
console.log(g.myDict);