package react-rules-of-hooks-ppx
Install
dune-project
Dependency
Authors
Maintainers
Sources
sha256=6b7fdca0f32c2f05f1480db5a0b85e0726cdb1ed3d7136054a4071b4d41a8188
sha512=18b51e6a70b6cfa31cc00e8f5601ed6fe85456c5388dc41f1cc7ed21830b8ca737f3e1dff47f8736aa5553b0e463f6f8dcac165e328b5c0a1590507e1bd3ed3d
doc/README.html
react-rules-of-hooks-ppx
A ppx that validates React's Rules of Hooks at compile time.
Features
- Exhaustive dependencies in useEffect, useCallback, useMemo, etc.
Order of Hooks validation:
- Hooks can't be called conditionally
- Hooks must be called at the top level
- Hooks can only be called from
[@react.component]functions or custom hooks
How it works
Exhaustive dependencies
Here we have a dummy react component:
[@react.component]
/* Recives a "randomProp" prop */
let make = (~randomProp) => {
let (show, setShow) = React.useState(() => false);
/* We have a useEffect that re-runs each time the state "show" changes it's value, and we only want to trigger the `setShow` when `randomProp` is true. */
React.useEffect1(
() => {
/* Since this effect relies on "randomProp" and is not present on the dependency array... it will re-run only when show changes, and not when randomProp changes and may cause undesired behaviour */
if (randomProp) {
setShow(prevShow => !prevShow);
}
None;
},
[|show|],
);
<div />;
};With this ppx, it will produce the following warning:
6 | React.useEffect1(
7 | () => {
8 | if (randomProp) {
9 | setShow(_ => !show);
10 | }
...
13 | None;
14 | },
15 | [|show|],
^^^^^^^^
Error (warning 22): exhaustive-deps: Missing 'randomProp' in the dependency array.
To suppress this warning, add [@disable_exhaustive_deps] before the expression
16 | ).Order of hooks
Hooks must be called at the top level of your component. Calling hooks inside conditionals, loops, or nested functions will produce an error that fails the build:
[@react.component]
let make = (~condition) => {
if (condition) {
let (state, _) = React.useState(() => 0); /* That's wrong */
();
};
<div />;
};This will produce the following error:
3 | if (condition) {
4 | let (state, _) = React.useState(() => 0);
^^^^^^^^^^^^^^^^^^^^^^^^
Error: Hooks can't be called conditionally and must be called at the top-level of your component. Move this hook call outside of conditionals, loops, or nested functions.Install
opam install react-rules-of-hooks-ppx --save-devAdd the ppx into the dune files
(preprocess (pps reason-react react-rules-of-hooks-ppx))Suppress warnings locally
If you need to suppress an exhaustive deps warning for a specific case (e.g., you intentionally want to omit a dependency), use the [@disable_exhaustive_deps] attribute.
In Reason (.re files):
[@disable_exhaustive_deps]
React.useEffect1(() => {
/* ... */
None
}, [|dep|]);In OCaml (.ml files):
(React.useEffect1 (fun () ->
(* ... *)
None
) [|dep|])[@disable_exhaustive_deps]Disable "Exhaustive dependencies in useEffect" on the library
(preprocess (pps reason-react react-rules-of-hooks-ppx -disable-exhaustive-deps))Disable "Order of Hooks" on the library
(preprocess (pps reason-react react-rules-of-hooks-ppx -disable-order-of-hooks))Enable automatic corrections for missing dependencies
This ppx can generate .ppx-corrected files with suggested fixes for missing dependencies. This integrates with dune's promotion workflow, allowing you to review and accept the suggested changes.
(preprocess (pps reason-react react-rules-of-hooks-ppx -corrections))When enabled, running the build will show a diff with the suggested fix:
[@react.component]
let make = (~dep1) => {
React.useEffect0(() => {
Js.log(dep1);
None;
});
<span />;
};- React.useEffect0(() => {
+ React.useEffect1(() => {
Js.log(dep1);
None;
- });
+ }, [| dep1 |]);You can then run dune promote to accept the corrections.
Issues
Feel free to use it and report any unexpected behaviour in the bug tracker
Acknowledgements
Thanks to @jchavarri