Quickwin good practices for ReasonReact

reasonreact

#1

I want to keep this thread active with small hints and tips for ReasonReact good practices.
Feel free to contribute with more snippets!

Dealing with options within JSX

Do

<>
{
 data##user
	->Belt.Option.map(user => <p>{React.string(user##name)}</p>)
	->Belt.Option.getWithDefault(React.null)
}
</>

Don’t

<>
{
 data##user
  ->Belt.Option.getWithDefault({ "name": "" })
  ->(user => {
    <p>{React.string(user##name)}</p>
  })
}
</>

I have seen some beginners prefer the latter when starting to code in Reason probably because they are used to obj || {name: ''} in JS but the first scales much better so you don’t have to change a default value if the shape of your data changes


#2

You can also use Belt.Option.mapWithDefault to combine the two steps into one:

<>
{
  data##user->Belt.Option.mapWithDefault(React.null, user =>
    <p>{React.string(user##name)}</p>
  )
}
</>

#3

Using React.null is so common that I have created a module Render with a map function, like this:

/**
 If [optionValue] is [Some(value)], returns [f(value)]; otherwise returns [React.null]
 */
let map: (option('a), 'a => React.element) => React.element =
  (opt, f) =>
    switch (opt) {
    | None => React.null
    | Some(item) => f(item)
    };

and then:

<>
{data##user->Render.map(user => <p>{React.string(user##name)}</p>)}
</>

and so on for get, List.getByIndex, …


#4

Another option/nullable situation: refs.

Here’s the helper:

let currentHtmlIter = (ref, ~f) => {
  ref
  ->React.Ref.current
  ->Js.Nullable.iter((. el) => el->Webapi.Dom.Element.unsafeAsHtmlElement->f);
};

Here’s a usage example:

  let presentParagraphRef = React.useRef(Js.Nullable.null);

  let presentParagraphIter = Ref.currentHtmlIter(presentParagraphRef);

  React.useEffect1(
    () => {
      presentParagraphIter(~f=Webapi.Dom.HtmlElement.focus);
      None;
    },
    [|state.past|],
  );

Whether or not ~f should be a labeled argument is debatable, of course. I prefer labeled because then the helper can be used both with fast pipes and also in its curried form, like above. What can I say, I like Jane Street Base’s approach where all but one argument are labeled :man_shrugging: