reasonreason-react

How to port following hook to reasonml


I have following custom hook

function useConstant(fn) {
  const ref = React.useRef()

  if (!ref.current) {
    ref.current = fn()
  }

  return ref.current
}

and it seems quite hard to port this to reasonml, I have to use type cast twice, what's the ideal way?

external toAny: 'a => 'b = "%identity";
external toBool: 'a => bool = "%identity";

let useConstant = (fn: unit => 'a) => {
  let ref: React.Ref.t('a) = toAny(React.useRef());
  if (!toBool(React.Ref.current(ref))) {
    React.Ref.setCurrent(ref, fn());
  };
  React.Ref.current(ref);
};

Solution

  • If I understand the purpose of the hook correctly, it's really just a reimplementation of React.useMemo. But for the sake of learning, here's an implementation that should work.

    let useLazy = (fn: unit => 'a): 'a => {
      let ref = React.useRef(None);
    
      switch (React.Ref.current(ref)) {
      | Some(value) => value
      | None =>
        let value = fn();
        React.Ref.setCurrent(ref, Some(value));
        value;
      };
    };
    

    It uses the option type, which is specifically designed for cases like this. If there's no value, we represent that using options None value, and if there is a value we use Some. Instead of using if with JavaScript's semantically unclear concept of truthiness, we pattern match on the option using switch to find that it's None and the value needs to be computed, or Some to get at the value.

    The use of option and pattern matching is really common in Reason code, so it's one you should really try to understand using the links provided above for more details if needed.

    Note that you could also have used Lazy for this. But that's far less commonly used and therefore also much less useful to learn.