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.
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 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);
};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);
};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));React.useEffect0(() => {
Js.log("mount");
/* must return one of the following */
None; /* no unmount */
Some(() => Js.log("unmount"));
});let myRef = React.useRef(initialValue);
/* getter */
let value = React.Ref.current(myRef);
/* setter */
React.Ref.setCurrent(myRef, newValue);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 />;
};