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);
};
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 option
s 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.