paul shenposts
ReasonReact hook recipes
ReasonReact's 0.7.0 release adds support for React hooks. It took me some googling and reading through React.re to figure out how to use them all. Here they are in one place with usage examples.
React context and useContext
This was the least obvious to me but makes sense if you understand what [@react.component] is doing.
type theme = Dark | Light;
let themeContext: React.Context.t(theme) = React.createContext(Dark);
module ThemeProvider = {
  let makeProps = (~value, ~children, ()) => {
    "value": value,
    "children": children,
  };
  let make = React.Context.provider(themeContext);
};
/* provider */
<ThemeProvider value=Light> children </ThemeProvider>;
/* consumer */
let make = () => {
  let theme = React.useContext(themeContext);
};
useState
useState requires using the lazy initializer and the `state => `state version of setState. Without this constraint, Reason can't tell if you are providing a lazy initializer or have state of type unit => 'a. The ReasonReact source says "we know this api isn't great. tl;dr useReducer instead" but I think it's quite usable.
let make = () => {
  let (value, setValue) = React.useState(() => initialValue);
  /* setValue: (`state => `state) => unit */
  setValue(oldValue => newValue);
};
useReducer
type action = Increment | Decrement;
let reducer = (state, action) =>
  switch (action) {
  | Increment => state + 1
  | Decrement => state - 1
  };
let make = () => {
  let (value, dispatch) = React.useReducer(reducer, 0);
  /* dispatch: `action => unit */
  dispatch(Increment);
};
Dependencies
useEffect, useLayoutEffect, useMemo, useCallback, and useImperativeHandle take dependencies. ReasonReact has distinct APIs for each length of dependencies. The variants are described once here with useMemo. The remainder core hooks with deps follow the same pattern.
/* no deps - almost never used */
React.useMemo(() => []);
/* useHook0 is equivalent to passing [] to deps */
React.useMemo0(() => []);
/* useHook1 is special in taking a list of deps */
React.useMemo1(() => [value], [|value|]);
/* heck you can use for any number of deps */
React.useMemo1(() => [value1, value2], [|value1, value2|]);
/* useHookN where N > 1 take a N-size tuple */
React.useMemo2(() => [arg1, arg2], (arg1, arg2));
React.useMemo3(() => [arg1, arg2, arg3], (arg1, arg2, arg3));
useEffect, useLayoutEffect
React.useEffect0(() => {
  Js.log("mount");
  /* must return one of the following */
  None; /* no unmount */
  Some(() => Js.log("unmount"));
});
useRef
let myRef = React.useRef(initialValue);
/* getter */
let value = React.Ref.current(myRef);
/* setter */
React.Ref.setCurrent(myRef, newValue);
forwardRef
module MyInput = {
  [@react.component]
  let make = React.forwardRef((~label, ~value, theRef) =>
    <div>
      <label> {React.string(label)} </label>
      <input
        value
        ref=?{Belt.Option.map(Js.Nullable.toOption(theRef), ReactDOMRe.Ref.domRef)}
      />
    </div>
  );
};
let make = () => {
  let ref = React.useRef(Js.Nullable.null);
  <MyInput label="Label" value="Value" ref />;
};