diff --git a/compiler/packages/babel-plugin-react-compiler/src/Validation/ValidateExhaustiveDependencies.ts b/compiler/packages/babel-plugin-react-compiler/src/Validation/ValidateExhaustiveDependencies.ts index 313f773e2945b..df85ea8648c7e 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/Validation/ValidateExhaustiveDependencies.ts +++ b/compiler/packages/babel-plugin-react-compiler/src/Validation/ValidateExhaustiveDependencies.ts @@ -261,6 +261,20 @@ export function validateExhaustiveDependencies( extra.push(dep); } + /* + * For compatiblity with the existing exhaustive-deps rule, we allow + * known-stable values as dependencies even if the value is not reactive. + * This allows code that takes a dep on a non-reactive setState function + * to pass, for example. + */ + retainWhere(extra, dep => { + const isNonReactiveStableValue = + dep.root.kind === 'NamedLocal' && + !dep.root.value.reactive && + isStableType(dep.root.value.identifier); + return !isNonReactiveStableValue; + }); + if (missing.length !== 0 || extra.length !== 0) { let suggestions: Array | null = null; if (startMemo.depsLoc != null && typeof startMemo.depsLoc !== 'symbol') { diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/exhaustive-deps-allow-nonreactive-stable-types-as-extra-deps.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/exhaustive-deps-allow-nonreactive-stable-types-as-extra-deps.expect.md new file mode 100644 index 0000000000000..5b6319ed7cf5e --- /dev/null +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/exhaustive-deps-allow-nonreactive-stable-types-as-extra-deps.expect.md @@ -0,0 +1,100 @@ + +## Input + +```javascript +// @validateExhaustiveMemoizationDependencies +import { + useCallback, + useTransition, + useState, + useOptimistic, + useActionState, + useRef, + useReducer, +} from 'react'; + +function useFoo() { + const [s, setState] = useState(); + const ref = useRef(null); + const [t, startTransition] = useTransition(); + const [u, addOptimistic] = useOptimistic(); + const [v, dispatch] = useReducer(() => {}, null); + const [isPending, dispatchAction] = useActionState(() => {}, null); + + return useCallback(() => { + dispatch(); + startTransition(() => {}); + addOptimistic(); + setState(null); + dispatchAction(); + ref.current = true; + }, [ + // intentionally adding unnecessary deps on nonreactive stable values + // to check that they're allowed + dispatch, + startTransition, + addOptimistic, + setState, + dispatchAction, + ref, + ]); +} + +export const FIXTURE_ENTRYPOINT = { + fn: useFoo, + params: [], +}; + +``` + +## Code + +```javascript +import { c as _c } from "react/compiler-runtime"; // @validateExhaustiveMemoizationDependencies +import { + useCallback, + useTransition, + useState, + useOptimistic, + useActionState, + useRef, + useReducer, +} from "react"; + +function useFoo() { + const $ = _c(1); + const [, setState] = useState(); + const ref = useRef(null); + const [, startTransition] = useTransition(); + const [, addOptimistic] = useOptimistic(); + const [, dispatch] = useReducer(_temp, null); + const [, dispatchAction] = useActionState(_temp2, null); + let t0; + if ($[0] === Symbol.for("react.memo_cache_sentinel")) { + t0 = () => { + dispatch(); + startTransition(_temp3); + addOptimistic(); + setState(null); + dispatchAction(); + ref.current = true; + }; + $[0] = t0; + } else { + t0 = $[0]; + } + return t0; +} +function _temp3() {} +function _temp2() {} +function _temp() {} + +export const FIXTURE_ENTRYPOINT = { + fn: useFoo, + params: [], +}; + +``` + +### Eval output +(kind: ok) "[[ function params=0 ]]" \ No newline at end of file diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/exhaustive-deps-allow-nonreactive-stable-types-as-extra-deps.js b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/exhaustive-deps-allow-nonreactive-stable-types-as-extra-deps.js new file mode 100644 index 0000000000000..71000afcc423b --- /dev/null +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/exhaustive-deps-allow-nonreactive-stable-types-as-extra-deps.js @@ -0,0 +1,42 @@ +// @validateExhaustiveMemoizationDependencies +import { + useCallback, + useTransition, + useState, + useOptimistic, + useActionState, + useRef, + useReducer, +} from 'react'; + +function useFoo() { + const [s, setState] = useState(); + const ref = useRef(null); + const [t, startTransition] = useTransition(); + const [u, addOptimistic] = useOptimistic(); + const [v, dispatch] = useReducer(() => {}, null); + const [isPending, dispatchAction] = useActionState(() => {}, null); + + return useCallback(() => { + dispatch(); + startTransition(() => {}); + addOptimistic(); + setState(null); + dispatchAction(); + ref.current = true; + }, [ + // intentionally adding unnecessary deps on nonreactive stable values + // to check that they're allowed + dispatch, + startTransition, + addOptimistic, + setState, + dispatchAction, + ref, + ]); +} + +export const FIXTURE_ENTRYPOINT = { + fn: useFoo, + params: [], +};