package react-rules-of-hooks-ppx
sectionYPositions = computeSectionYPositions($el), 10)"
x-init="setTimeout(() => sectionYPositions = computeSectionYPositions($el), 10)"
>
On This Page
This ppx validates the rules of React hooks in reason-react components
Install
dune-project
Dependency
Authors
Maintainers
Sources
react-rules-of-hooks-ppx-1.4.0.tbz
sha256=75f5e3d4926ee92c9b95765e29208f1ae81c7130871a26914867d21d6829d719
sha512=13bfdf55f90e7201657188b84dcedafb1cc4b8f2f2a173fdb53cd98ddf427e43bb9e94aa105cd20576ebbd31fc680dd367835d78d06941e84a607793b2bc5086
doc/CHANGES.html
Changes
1.4.0
- [FEAT]
[@disable_order_of_hooks]and[@disable_exhaustive_deps]on aletbinding (let[@disable_order_of_hooks] x = ...) skip their checks for every hook call in that binding. - [FIX] A hook call after a JSX element in a sequence (
ignore <div />; React.useState ...) is no longer reported as called conditionally.
1.3.0
Exhaustive dependencies
- [FEAT] Member-path dependency tracking, as in eslint-plugin-react-hooks. Dependencies and body uses are compared as full member paths, so
(input.page, input.limit)is no longer reported as a duplicate, and deps[|input.limit|]over a body readinginput.pagenow report a missinginput.page. A declaredinput.pagecovers deeper uses (input.page.size) but not the whole record. Stable roots (setters, refs) keep every path under them exempt (ref.current). Diagnostics and-correctionsoutput emit member paths. - [FEAT] Automatic stable-hook detection: a same-file
use*wrapper whose body is directly auseState/useReducer/useRefapplication inherits its stability (chains resolve transitively), solet useStateValue = initial => useReducer(...)setters stop triggering missing-deps warnings at every use site. - [FEAT] Setter naming convention: when destructuring any hook call result, a second tuple element or record field named
set[A-Z]...,set_..., ordispatch...is treated as stable. Plain closures, whole-return bindings, and non-conventional names are still checked. Generated-correctionsnever include exempted values. - [FIX] Effects without a dependency array (
React.useEffect(fn),useLayoutEffect,useInsertionEffect) no longer report missing dependencies: they run after every render, so nothing can be stale. Matches eslint-plugin-react-hooks. Order-of-hooks checks still apply to these calls and their callbacks. - [FIX] Identifiers in
whenguards ofmatch/trycases inside a hook callback now count as dependency uses:match v with Some x when x > limit -> ...reports a missinglimit. Previously onlyfunctioncase guards were collected. - [FIX] Member paths in call position now count as dependency uses:
input.callback ()reports a missinginput.callback, matching eslint-plugin-react-hooks. Callees that are not plain paths (e.g.(getHandlers ()).onClick ()) degrade to their sub-expressions, sogetHandlersis tracked. Previously only bare-identifier callees were collected. - [FIX]
-disable-order-of-hooksno longer silently degrades exhaustive-deps: binding scope tracking (component-scope bindings, outer-scope bindings, stable hooks) now runs regardless of the flag, so missing-dependency and outer-scope warnings are still reported when only order-of-hooks checking is disabled.
New diagnostics
- [FEAT] A direct
useState/useReducersetter call inside a no-deps effect warns about an infinite chain of updates (setters inside nested functions are fine). - [FEAT] Unsuffixed
useMemo/useCallbackwarn that the memoization does nothing without a dependency array, instead of demanding deps the call cannot take. Both diagnostics can appear at previously silent sites, which matters when bumping under warnings-as-errors.
Compatibility with server-reason-react
- [FEAT]
[@react.client.component]bindings are recognized as components. - [FEAT]
[@react.async.component]bindings are recognized as components, same as[@react.component]. - [FEAT] Understand server-reason-react platform branches: hooks inside
switch%platform/match%platformbranches are no longer flagged as conditional (branches are resolved at compile time, one per build target). Runtime conditionals nested inside a branch, or wrapping the switch, still error. - [FEAT]
let x = switch%platform ...bindings whose branches are functions classify as components/custom hooks/functions by the usual name and attribute rules (per-target hook implementations, e.g.let use = switch%platform () { | Server => Context.use | Client => ReasonReactRouter.useUrl }). - [FEAT]
let%browser_only-bound functions whose bodies call hooks are treated as custom hooks regardless of name, and their call sites are linted like hook calls (conditional call = error, with[@disable_order_of_hooks]as the opt-out). Hook-free%browser_onlyutilities are unaffected and may still be called conditionally. - [FEAT] Exhaustive-deps sees through
[%browser_only ...]payloads. This can surface new missing-deps lint warnings in code deps analysis could not see before. - [FEAT] Exhaustive-deps considers only the
Clientbranch ofswitch%platform(dependency arrays only drive behavior in the client bundle);useState/useReducer/useRefresults bound through aClientbranch register as stable deps.
1.2.0
- [FEAT] Support
use+ numbers as a valid hook name - [FIX] Outer scope bindings (module-level values and functions) no longer trigger missing dependency warnings in exhaustive deps checks
1.1.0
- [FEAT] Disable order of hooks attribute
[@disable_order_of_hooks] - [CHORE] Add mlx as
:with-testand:with-dev-setup(previously only:with-dev-setup) - [FIX] Add support for snake_case hooks (
use_state,use_effect,use_custom_hook) - [FIX] SVG
<use>element no longer incorrectly flagged as a hook (JSX elements excluded from hook detection) - [FIX] False positive when multiple hooks are defined
- [FIX] Hooks name can be "use"
- [FIX] Fix static deps scope leaking between components (useState setters, useReducer dispatchers, useRef results now properly scoped per component)
- [FIX] JSX context reset bug where multiple hooks in the same JSX element weren't all flagged as violations
- [FEAT] Add
Pexp_letopsupport for monadiclet+/and+syntax in exhaustive deps checking - [TEST] Add test cases inspired by React's eslint-plugin-react-hooks
- [FEAT] Add
REACT_HOOKS_PPX_TIMINGenv var to print timing diagnostics - [FIX] Use Set for O(log n) lookups, single-pass AST analysis, caching
- [FIX] Simplify diff function
- [FIX] Replace
List.length > 0with<> [] - [FIX] Optimize
find_duplicatesto deduplicate during traversal
1.0.0
- [FEAT] Detect hooks called conditionally, in loops, or in nested functions
- [FEAT] Detect hooks called outside of
[@react.component]functions or custom hooks - [FEAT] Check exhaustive dependencies in
useEffect,useMemo,useCallback,useLayoutEffect, anduseInsertionEffect - [FEAT] Disable order of hooks check globally with
-order-of-hooksppx flag - [FEAT] Disable exhaustive deps check globally with
-exhaustive-depsppx flag - [FEAT] Suppress exhaustive deps warning locally with
[@disable_exhaustive_deps]attribute - [FEAT]
-correctionsflag to generate.ppx-correctedfiles with suggested fixes for missing dependencies - [FIX] Improve
-correctionsaccording to the reason-react interface
sectionYPositions = computeSectionYPositions($el), 10)"
x-init="setTimeout(() => sectionYPositions = computeSectionYPositions($el), 10)"
>
On This Page