1. /**
    
  2.  * Copyright (c) Meta Platforms, Inc. and affiliates.
    
  3.  *
    
  4.  * This source code is licensed under the MIT license found in the
    
  5.  * LICENSE file in the root directory of this source tree.
    
  6.  */
    
  7. 
    
  8. 'use strict';
    
  9. 
    
  10. const ESLintTester = require('eslint').RuleTester;
    
  11. const ReactHooksESLintPlugin = require('eslint-plugin-react-hooks');
    
  12. const ReactHooksESLintRule = ReactHooksESLintPlugin.rules['exhaustive-deps'];
    
  13. 
    
  14. /**
    
  15.  * A string template tag that removes padding from the left side of multi-line strings
    
  16.  * @param {Array} strings array of code strings (only one expected)
    
  17.  */
    
  18. function normalizeIndent(strings) {
    
  19.   const codeLines = strings[0].split('\n');
    
  20.   const leftPadding = codeLines[1].match(/\s+/)[0];
    
  21.   return codeLines.map(line => line.slice(leftPadding.length)).join('\n');
    
  22. }
    
  23. 
    
  24. // ***************************************************
    
  25. // For easier local testing, you can add to any case:
    
  26. // {
    
  27. //   skip: true,
    
  28. //   --or--
    
  29. //   only: true,
    
  30. //   ...
    
  31. // }
    
  32. // ***************************************************
    
  33. 
    
  34. // Tests that are valid/invalid across all parsers
    
  35. const tests = {
    
  36.   valid: [
    
  37.     {
    
  38.       code: normalizeIndent`
    
  39.         function MyComponent() {
    
  40.           const local = {};
    
  41.           useEffect(() => {
    
  42.             console.log(local);
    
  43.           });
    
  44.         }
    
  45.       `,
    
  46.     },
    
  47.     {
    
  48.       code: normalizeIndent`
    
  49.         function MyComponent() {
    
  50.           useEffect(() => {
    
  51.             const local = {};
    
  52.             console.log(local);
    
  53.           }, []);
    
  54.         }
    
  55.       `,
    
  56.     },
    
  57.     {
    
  58.       code: normalizeIndent`
    
  59.         function MyComponent() {
    
  60.           const local = someFunc();
    
  61.           useEffect(() => {
    
  62.             console.log(local);
    
  63.           }, [local]);
    
  64.         }
    
  65.       `,
    
  66.     },
    
  67.     {
    
  68.       // OK because `props` wasn't defined.
    
  69.       // We don't technically know if `props` is supposed
    
  70.       // to be an import that hasn't been added yet, or
    
  71.       // a component-level variable. Ignore it until it
    
  72.       //  gets defined (a different rule would flag it anyway).
    
  73.       code: normalizeIndent`
    
  74.         function MyComponent() {
    
  75.           useEffect(() => {
    
  76.             console.log(props.foo);
    
  77.           }, []);
    
  78.         }
    
  79.       `,
    
  80.     },
    
  81.     {
    
  82.       code: normalizeIndent`
    
  83.         function MyComponent() {
    
  84.           const local1 = {};
    
  85.           {
    
  86.             const local2 = {};
    
  87.             useEffect(() => {
    
  88.               console.log(local1);
    
  89.               console.log(local2);
    
  90.             });
    
  91.           }
    
  92.         }
    
  93.       `,
    
  94.     },
    
  95.     {
    
  96.       code: normalizeIndent`
    
  97.         function MyComponent() {
    
  98.           const local1 = someFunc();
    
  99.           {
    
  100.             const local2 = someFunc();
    
  101.             useCallback(() => {
    
  102.               console.log(local1);
    
  103.               console.log(local2);
    
  104.             }, [local1, local2]);
    
  105.           }
    
  106.         }
    
  107.       `,
    
  108.     },
    
  109.     {
    
  110.       code: normalizeIndent`
    
  111.         function MyComponent() {
    
  112.           const local1 = someFunc();
    
  113.           function MyNestedComponent() {
    
  114.             const local2 = someFunc();
    
  115.             useCallback(() => {
    
  116.               console.log(local1);
    
  117.               console.log(local2);
    
  118.             }, [local2]);
    
  119.           }
    
  120.         }
    
  121.       `,
    
  122.     },
    
  123.     {
    
  124.       code: normalizeIndent`
    
  125.         function MyComponent() {
    
  126.           const local = someFunc();
    
  127.           useEffect(() => {
    
  128.             console.log(local);
    
  129.             console.log(local);
    
  130.           }, [local]);
    
  131.         }
    
  132.       `,
    
  133.     },
    
  134.     {
    
  135.       code: normalizeIndent`
    
  136.         function MyComponent() {
    
  137.           useEffect(() => {
    
  138.             console.log(unresolved);
    
  139.           }, []);
    
  140.         }
    
  141.       `,
    
  142.     },
    
  143.     {
    
  144.       code: normalizeIndent`
    
  145.         function MyComponent() {
    
  146.           const local = someFunc();
    
  147.           useEffect(() => {
    
  148.             console.log(local);
    
  149.           }, [,,,local,,,]);
    
  150.         }
    
  151.       `,
    
  152.     },
    
  153.     {
    
  154.       // Regression test
    
  155.       code: normalizeIndent`
    
  156.         function MyComponent({ foo }) {
    
  157.           useEffect(() => {
    
  158.             console.log(foo.length);
    
  159.           }, [foo]);
    
  160.         }
    
  161.       `,
    
  162.     },
    
  163.     {
    
  164.       // Regression test
    
  165.       code: normalizeIndent`
    
  166.         function MyComponent({ foo }) {
    
  167.           useEffect(() => {
    
  168.             console.log(foo.length);
    
  169.             console.log(foo.slice(0));
    
  170.           }, [foo]);
    
  171.         }
    
  172.       `,
    
  173.     },
    
  174.     {
    
  175.       // Regression test
    
  176.       code: normalizeIndent`
    
  177.         function MyComponent({ history }) {
    
  178.           useEffect(() => {
    
  179.             return history.listen();
    
  180.           }, [history]);
    
  181.         }
    
  182.       `,
    
  183.     },
    
  184.     {
    
  185.       // Valid because they have meaning without deps.
    
  186.       code: normalizeIndent`
    
  187.         function MyComponent(props) {
    
  188.           useEffect(() => {});
    
  189.           useLayoutEffect(() => {});
    
  190.           useImperativeHandle(props.innerRef, () => {});
    
  191.         }
    
  192.       `,
    
  193.     },
    
  194.     {
    
  195.       code: normalizeIndent`
    
  196.         function MyComponent(props) {
    
  197.           useEffect(() => {
    
  198.             console.log(props.foo);
    
  199.           }, [props.foo]);
    
  200.         }
    
  201.       `,
    
  202.     },
    
  203.     {
    
  204.       code: normalizeIndent`
    
  205.         function MyComponent(props) {
    
  206.           useEffect(() => {
    
  207.             console.log(props.foo);
    
  208.             console.log(props.bar);
    
  209.           }, [props.bar, props.foo]);
    
  210.         }
    
  211.       `,
    
  212.     },
    
  213.     {
    
  214.       code: normalizeIndent`
    
  215.         function MyComponent(props) {
    
  216.           useEffect(() => {
    
  217.             console.log(props.foo);
    
  218.             console.log(props.bar);
    
  219.           }, [props.foo, props.bar]);
    
  220.         }
    
  221.       `,
    
  222.     },
    
  223.     {
    
  224.       code: normalizeIndent`
    
  225.         function MyComponent(props) {
    
  226.           const local = someFunc();
    
  227.           useEffect(() => {
    
  228.             console.log(props.foo);
    
  229.             console.log(props.bar);
    
  230.             console.log(local);
    
  231.           }, [props.foo, props.bar, local]);
    
  232.         }
    
  233.       `,
    
  234.     },
    
  235.     {
    
  236.       // [props, props.foo] is technically unnecessary ('props' covers 'props.foo').
    
  237.       // However, it's valid for effects to over-specify their deps.
    
  238.       // So we don't warn about this. We *would* warn about useMemo/useCallback.
    
  239.       code: normalizeIndent`
    
  240.         function MyComponent(props) {
    
  241.           const local = {};
    
  242.           useEffect(() => {
    
  243.             console.log(props.foo);
    
  244.             console.log(props.bar);
    
  245.           }, [props, props.foo]);
    
  246. 
    
  247.           let color = someFunc();
    
  248.           useEffect(() => {
    
  249.             console.log(props.foo.bar.baz);
    
  250.             console.log(color);
    
  251.           }, [props.foo, props.foo.bar.baz, color]);
    
  252.         }
    
  253.       `,
    
  254.     },
    
  255.     // Nullish coalescing and optional chaining
    
  256.     {
    
  257.       code: normalizeIndent`
    
  258.         function MyComponent(props) {
    
  259.           useEffect(() => {
    
  260.             console.log(props.foo?.bar?.baz ?? null);
    
  261.           }, [props.foo]);
    
  262.         }
    
  263.       `,
    
  264.     },
    
  265.     {
    
  266.       code: normalizeIndent`
    
  267.         function MyComponent(props) {
    
  268.           useEffect(() => {
    
  269.             console.log(props.foo?.bar);
    
  270.           }, [props.foo?.bar]);
    
  271.         }
    
  272.       `,
    
  273.     },
    
  274.     {
    
  275.       code: normalizeIndent`
    
  276.         function MyComponent(props) {
    
  277.           useEffect(() => {
    
  278.             console.log(props.foo?.bar);
    
  279.           }, [props.foo.bar]);
    
  280.         }
    
  281.       `,
    
  282.     },
    
  283.     {
    
  284.       code: normalizeIndent`
    
  285.         function MyComponent(props) {
    
  286.           useEffect(() => {
    
  287.             console.log(props.foo.bar);
    
  288.           }, [props.foo?.bar]);
    
  289.         }
    
  290.       `,
    
  291.     },
    
  292.     {
    
  293.       code: normalizeIndent`
    
  294.         function MyComponent(props) {
    
  295.           useEffect(() => {
    
  296.             console.log(props.foo.bar);
    
  297.             console.log(props.foo?.bar);
    
  298.           }, [props.foo?.bar]);
    
  299.         }
    
  300.       `,
    
  301.     },
    
  302.     {
    
  303.       code: normalizeIndent`
    
  304.         function MyComponent(props) {
    
  305.           useEffect(() => {
    
  306.             console.log(props.foo.bar);
    
  307.             console.log(props.foo?.bar);
    
  308.           }, [props.foo.bar]);
    
  309.         }
    
  310.       `,
    
  311.     },
    
  312.     {
    
  313.       code: normalizeIndent`
    
  314.         function MyComponent(props) {
    
  315.           useEffect(() => {
    
  316.             console.log(props.foo);
    
  317.             console.log(props.foo?.bar);
    
  318.           }, [props.foo]);
    
  319.         }
    
  320.       `,
    
  321.     },
    
  322.     {
    
  323.       code: normalizeIndent`
    
  324.         function MyComponent(props) {
    
  325.           useEffect(() => {
    
  326.             console.log(props.foo?.toString());
    
  327.           }, [props.foo]);
    
  328.         }
    
  329.       `,
    
  330.     },
    
  331.     {
    
  332.       code: normalizeIndent`
    
  333.         function MyComponent(props) {
    
  334.           useMemo(() => {
    
  335.             console.log(props.foo?.toString());
    
  336.           }, [props.foo]);
    
  337.         }
    
  338.       `,
    
  339.     },
    
  340.     {
    
  341.       code: normalizeIndent`
    
  342.         function MyComponent(props) {
    
  343.           useCallback(() => {
    
  344.             console.log(props.foo?.toString());
    
  345.           }, [props.foo]);
    
  346.         }
    
  347.       `,
    
  348.     },
    
  349.     {
    
  350.       code: normalizeIndent`
    
  351.         function MyComponent(props) {
    
  352.           useCallback(() => {
    
  353.             console.log(props.foo.bar?.toString());
    
  354.           }, [props.foo.bar]);
    
  355.         }
    
  356.       `,
    
  357.     },
    
  358.     {
    
  359.       code: normalizeIndent`
    
  360.         function MyComponent(props) {
    
  361.           useCallback(() => {
    
  362.             console.log(props.foo?.bar?.toString());
    
  363.           }, [props.foo.bar]);
    
  364.         }
    
  365.       `,
    
  366.     },
    
  367.     {
    
  368.       code: normalizeIndent`
    
  369.         function MyComponent(props) {
    
  370.           useCallback(() => {
    
  371.             console.log(props.foo.bar.toString());
    
  372.           }, [props?.foo?.bar]);
    
  373.         }
    
  374.       `,
    
  375.     },
    
  376.     {
    
  377.       code: normalizeIndent`
    
  378.         function MyComponent(props) {
    
  379.           useCallback(() => {
    
  380.             console.log(props.foo?.bar?.baz);
    
  381.           }, [props?.foo.bar?.baz]);
    
  382.         }
    
  383.       `,
    
  384.     },
    
  385.     {
    
  386.       code: normalizeIndent`
    
  387.         function MyComponent() {
    
  388.           const myEffect = () => {
    
  389.             // Doesn't use anything
    
  390.           };
    
  391.           useEffect(myEffect, []);
    
  392.         }
    
  393.       `,
    
  394.     },
    
  395.     {
    
  396.       code: normalizeIndent`
    
  397.         const local = {};
    
  398.         function MyComponent() {
    
  399.           const myEffect = () => {
    
  400.             console.log(local);
    
  401.           };
    
  402.           useEffect(myEffect, []);
    
  403.         }
    
  404.       `,
    
  405.     },
    
  406.     {
    
  407.       code: normalizeIndent`
    
  408.         const local = {};
    
  409.         function MyComponent() {
    
  410.           function myEffect() {
    
  411.             console.log(local);
    
  412.           }
    
  413.           useEffect(myEffect, []);
    
  414.         }
    
  415.       `,
    
  416.     },
    
  417.     {
    
  418.       code: normalizeIndent`
    
  419.         function MyComponent() {
    
  420.           const local = someFunc();
    
  421.           function myEffect() {
    
  422.             console.log(local);
    
  423.           }
    
  424.           useEffect(myEffect, [local]);
    
  425.         }
    
  426.       `,
    
  427.     },
    
  428.     {
    
  429.       code: normalizeIndent`
    
  430.         function MyComponent() {
    
  431.           function myEffect() {
    
  432.             console.log(global);
    
  433.           }
    
  434.           useEffect(myEffect, []);
    
  435.         }
    
  436.       `,
    
  437.     },
    
  438.     {
    
  439.       code: normalizeIndent`
    
  440.         const local = {};
    
  441.         function MyComponent() {
    
  442.           const myEffect = () => {
    
  443.             otherThing()
    
  444.           }
    
  445.           const otherThing = () => {
    
  446.             console.log(local);
    
  447.           }
    
  448.           useEffect(myEffect, []);
    
  449.         }
    
  450.       `,
    
  451.     },
    
  452.     {
    
  453.       // Valid because even though we don't inspect the function itself,
    
  454.       // at least it's passed as a dependency.
    
  455.       code: normalizeIndent`
    
  456.         function MyComponent({delay}) {
    
  457.           const local = {};
    
  458.           const myEffect = debounce(() => {
    
  459.             console.log(local);
    
  460.           }, delay);
    
  461.           useEffect(myEffect, [myEffect]);
    
  462.         }
    
  463.       `,
    
  464.     },
    
  465.     {
    
  466.       code: normalizeIndent`
    
  467.         function MyComponent({myEffect}) {
    
  468.           useEffect(myEffect, [,myEffect]);
    
  469.         }
    
  470.       `,
    
  471.     },
    
  472.     {
    
  473.       code: normalizeIndent`
    
  474.         function MyComponent({myEffect}) {
    
  475.           useEffect(myEffect, [,myEffect,,]);
    
  476.         }
    
  477.       `,
    
  478.     },
    
  479.     {
    
  480.       code: normalizeIndent`
    
  481.         let local = {};
    
  482.         function myEffect() {
    
  483.           console.log(local);
    
  484.         }
    
  485.         function MyComponent() {
    
  486.           useEffect(myEffect, []);
    
  487.         }
    
  488.       `,
    
  489.     },
    
  490.     {
    
  491.       code: normalizeIndent`
    
  492.         function MyComponent({myEffect}) {
    
  493.           useEffect(myEffect, [myEffect]);
    
  494.         }
    
  495.       `,
    
  496.     },
    
  497.     {
    
  498.       // Valid because has no deps.
    
  499.       code: normalizeIndent`
    
  500.         function MyComponent({myEffect}) {
    
  501.           useEffect(myEffect);
    
  502.         }
    
  503.       `,
    
  504.     },
    
  505.     {
    
  506.       code: normalizeIndent`
    
  507.         function MyComponent(props) {
    
  508.           useCustomEffect(() => {
    
  509.             console.log(props.foo);
    
  510.           });
    
  511.         }
    
  512.       `,
    
  513.       options: [{additionalHooks: 'useCustomEffect'}],
    
  514.     },
    
  515.     {
    
  516.       code: normalizeIndent`
    
  517.         function MyComponent(props) {
    
  518.           useCustomEffect(() => {
    
  519.             console.log(props.foo);
    
  520.           }, [props.foo]);
    
  521.         }
    
  522.       `,
    
  523.       options: [{additionalHooks: 'useCustomEffect'}],
    
  524.     },
    
  525.     {
    
  526.       code: normalizeIndent`
    
  527.         function MyComponent(props) {
    
  528.           useCustomEffect(() => {
    
  529.             console.log(props.foo);
    
  530.           }, []);
    
  531.         }
    
  532.       `,
    
  533.       options: [{additionalHooks: 'useAnotherEffect'}],
    
  534.     },
    
  535.     {
    
  536.       code: normalizeIndent`
    
  537.         function MyComponent(props) {
    
  538.           useWithoutEffectSuffix(() => {
    
  539.             console.log(props.foo);
    
  540.           }, []);
    
  541.         }
    
  542.       `,
    
  543.     },
    
  544.     {
    
  545.       code: normalizeIndent`
    
  546.         function MyComponent(props) {
    
  547.           return renderHelperConfusedWithEffect(() => {
    
  548.             console.log(props.foo);
    
  549.           }, []);
    
  550.         }
    
  551.       `,
    
  552.     },
    
  553.     {
    
  554.       // Valid because we don't care about hooks outside of components.
    
  555.       code: normalizeIndent`
    
  556.         const local = {};
    
  557.         useEffect(() => {
    
  558.           console.log(local);
    
  559.         }, []);
    
  560.       `,
    
  561.     },
    
  562.     {
    
  563.       // Valid because we don't care about hooks outside of components.
    
  564.       code: normalizeIndent`
    
  565.         const local1 = {};
    
  566.         {
    
  567.           const local2 = {};
    
  568.           useEffect(() => {
    
  569.             console.log(local1);
    
  570.             console.log(local2);
    
  571.           }, []);
    
  572.         }
    
  573.       `,
    
  574.     },
    
  575.     {
    
  576.       code: normalizeIndent`
    
  577.         function MyComponent() {
    
  578.           const ref = useRef();
    
  579.           useEffect(() => {
    
  580.             console.log(ref.current);
    
  581.           }, [ref]);
    
  582.         }
    
  583.       `,
    
  584.     },
    
  585.     {
    
  586.       code: normalizeIndent`
    
  587.         function MyComponent() {
    
  588.           const ref = useRef();
    
  589.           useEffect(() => {
    
  590.             console.log(ref.current);
    
  591.           }, []);
    
  592.         }
    
  593.       `,
    
  594.     },
    
  595.     {
    
  596.       code: normalizeIndent`
    
  597.         function MyComponent({ maybeRef2, foo }) {
    
  598.           const definitelyRef1 = useRef();
    
  599.           const definitelyRef2 = useRef();
    
  600.           const maybeRef1 = useSomeOtherRefyThing();
    
  601.           const [state1, setState1] = useState();
    
  602.           const [state2, setState2] = React.useState();
    
  603.           const [state3, dispatch1] = useReducer();
    
  604.           const [state4, dispatch2] = React.useReducer();
    
  605.           const [state5, maybeSetState] = useFunnyState();
    
  606.           const [state6, maybeDispatch] = useFunnyReducer();
    
  607.           const [isPending1] = useTransition();
    
  608.           const [isPending2, startTransition2] = useTransition();
    
  609.           const [isPending3] = React.useTransition();
    
  610.           const [isPending4, startTransition4] = React.useTransition();
    
  611.           const mySetState = useCallback(() => {}, []);
    
  612.           let myDispatch = useCallback(() => {}, []);
    
  613. 
    
  614.           useEffect(() => {
    
  615.             // Known to be static
    
  616.             console.log(definitelyRef1.current);
    
  617.             console.log(definitelyRef2.current);
    
  618.             console.log(maybeRef1.current);
    
  619.             console.log(maybeRef2.current);
    
  620.             setState1();
    
  621.             setState2();
    
  622.             dispatch1();
    
  623.             dispatch2();
    
  624.             startTransition1();
    
  625.             startTransition2();
    
  626.             startTransition3();
    
  627.             startTransition4();
    
  628. 
    
  629.             // Dynamic
    
  630.             console.log(state1);
    
  631.             console.log(state2);
    
  632.             console.log(state3);
    
  633.             console.log(state4);
    
  634.             console.log(state5);
    
  635.             console.log(state6);
    
  636.             console.log(isPending2);
    
  637.             console.log(isPending4);
    
  638.             mySetState();
    
  639.             myDispatch();
    
  640. 
    
  641.             // Not sure; assume dynamic
    
  642.             maybeSetState();
    
  643.             maybeDispatch();
    
  644.           }, [
    
  645.             // Dynamic
    
  646.             state1, state2, state3, state4, state5, state6,
    
  647.             maybeRef1, maybeRef2,
    
  648.             isPending2, isPending4,
    
  649. 
    
  650.             // Not sure; assume dynamic
    
  651.             mySetState, myDispatch,
    
  652.             maybeSetState, maybeDispatch
    
  653. 
    
  654.             // In this test, we don't specify static deps.
    
  655.             // That should be okay.
    
  656.           ]);
    
  657.         }
    
  658.       `,
    
  659.     },
    
  660.     {
    
  661.       code: normalizeIndent`
    
  662.         function MyComponent({ maybeRef2 }) {
    
  663.           const definitelyRef1 = useRef();
    
  664.           const definitelyRef2 = useRef();
    
  665.           const maybeRef1 = useSomeOtherRefyThing();
    
  666. 
    
  667.           const [state1, setState1] = useState();
    
  668.           const [state2, setState2] = React.useState();
    
  669.           const [state3, dispatch1] = useReducer();
    
  670.           const [state4, dispatch2] = React.useReducer();
    
  671. 
    
  672.           const [state5, maybeSetState] = useFunnyState();
    
  673.           const [state6, maybeDispatch] = useFunnyReducer();
    
  674. 
    
  675.           const mySetState = useCallback(() => {}, []);
    
  676.           let myDispatch = useCallback(() => {}, []);
    
  677. 
    
  678.           useEffect(() => {
    
  679.             // Known to be static
    
  680.             console.log(definitelyRef1.current);
    
  681.             console.log(definitelyRef2.current);
    
  682.             console.log(maybeRef1.current);
    
  683.             console.log(maybeRef2.current);
    
  684.             setState1();
    
  685.             setState2();
    
  686.             dispatch1();
    
  687.             dispatch2();
    
  688. 
    
  689.             // Dynamic
    
  690.             console.log(state1);
    
  691.             console.log(state2);
    
  692.             console.log(state3);
    
  693.             console.log(state4);
    
  694.             console.log(state5);
    
  695.             console.log(state6);
    
  696.             mySetState();
    
  697.             myDispatch();
    
  698. 
    
  699.             // Not sure; assume dynamic
    
  700.             maybeSetState();
    
  701.             maybeDispatch();
    
  702.           }, [
    
  703.             // Dynamic
    
  704.             state1, state2, state3, state4, state5, state6,
    
  705.             maybeRef1, maybeRef2,
    
  706. 
    
  707.             // Not sure; assume dynamic
    
  708.             mySetState, myDispatch,
    
  709.             maybeSetState, maybeDispatch,
    
  710. 
    
  711.             // In this test, we specify static deps.
    
  712.             // That should be okay too!
    
  713.             definitelyRef1, definitelyRef2, setState1, setState2, dispatch1, dispatch2
    
  714.           ]);
    
  715.         }
    
  716.       `,
    
  717.     },
    
  718.     {
    
  719.       code: normalizeIndent`
    
  720.         const MyComponent = forwardRef((props, ref) => {
    
  721.           useImperativeHandle(ref, () => ({
    
  722.             focus() {
    
  723.               alert(props.hello);
    
  724.             }
    
  725.           }))
    
  726.         });
    
  727.       `,
    
  728.     },
    
  729.     {
    
  730.       code: normalizeIndent`
    
  731.         const MyComponent = forwardRef((props, ref) => {
    
  732.           useImperativeHandle(ref, () => ({
    
  733.             focus() {
    
  734.               alert(props.hello);
    
  735.             }
    
  736.           }), [props.hello])
    
  737.         });
    
  738.       `,
    
  739.     },
    
  740.     {
    
  741.       // This is not ideal but warning would likely create
    
  742.       // too many false positives. We do, however, prevent
    
  743.       // direct assignments.
    
  744.       code: normalizeIndent`
    
  745.         function MyComponent(props) {
    
  746.           let obj = someFunc();
    
  747.           useEffect(() => {
    
  748.             obj.foo = true;
    
  749.           }, [obj]);
    
  750.         }
    
  751.       `,
    
  752.     },
    
  753.     {
    
  754.       code: normalizeIndent`
    
  755.         function MyComponent(props) {
    
  756.           let foo = {}
    
  757.           useEffect(() => {
    
  758.             foo.bar.baz = 43;
    
  759.           }, [foo.bar]);
    
  760.         }
    
  761.       `,
    
  762.     },
    
  763.     {
    
  764.       // Valid because we assign ref.current
    
  765.       // ourselves. Therefore it's likely not
    
  766.       // a ref managed by React.
    
  767.       code: normalizeIndent`
    
  768.         function MyComponent() {
    
  769.           const myRef = useRef();
    
  770.           useEffect(() => {
    
  771.             const handleMove = () => {};
    
  772.             myRef.current = {};
    
  773.             return () => {
    
  774.               console.log(myRef.current.toString())
    
  775.             };
    
  776.           }, []);
    
  777.           return <div />;
    
  778.         }
    
  779.       `,
    
  780.     },
    
  781.     {
    
  782.       // Valid because we assign ref.current
    
  783.       // ourselves. Therefore it's likely not
    
  784.       // a ref managed by React.
    
  785.       code: normalizeIndent`
    
  786.         function MyComponent() {
    
  787.           const myRef = useRef();
    
  788.           useEffect(() => {
    
  789.             const handleMove = () => {};
    
  790.             myRef.current = {};
    
  791.             return () => {
    
  792.               console.log(myRef?.current?.toString())
    
  793.             };
    
  794.           }, []);
    
  795.           return <div />;
    
  796.         }
    
  797.       `,
    
  798.     },
    
  799.     {
    
  800.       // Valid because we assign ref.current
    
  801.       // ourselves. Therefore it's likely not
    
  802.       // a ref managed by React.
    
  803.       code: normalizeIndent`
    
  804.         function useMyThing(myRef) {
    
  805.           useEffect(() => {
    
  806.             const handleMove = () => {};
    
  807.             myRef.current = {};
    
  808.             return () => {
    
  809.               console.log(myRef.current.toString())
    
  810.             };
    
  811.           }, [myRef]);
    
  812.         }
    
  813.       `,
    
  814.     },
    
  815.     {
    
  816.       // Valid because the ref is captured.
    
  817.       code: normalizeIndent`
    
  818.         function MyComponent() {
    
  819.           const myRef = useRef();
    
  820.           useEffect(() => {
    
  821.             const handleMove = () => {};
    
  822.             const node = myRef.current;
    
  823.             node.addEventListener('mousemove', handleMove);
    
  824.             return () => node.removeEventListener('mousemove', handleMove);
    
  825.           }, []);
    
  826.           return <div ref={myRef} />;
    
  827.         }
    
  828.       `,
    
  829.     },
    
  830.     {
    
  831.       // Valid because the ref is captured.
    
  832.       code: normalizeIndent`
    
  833.         function useMyThing(myRef) {
    
  834.           useEffect(() => {
    
  835.             const handleMove = () => {};
    
  836.             const node = myRef.current;
    
  837.             node.addEventListener('mousemove', handleMove);
    
  838.             return () => node.removeEventListener('mousemove', handleMove);
    
  839.           }, [myRef]);
    
  840.           return <div ref={myRef} />;
    
  841.         }
    
  842.       `,
    
  843.     },
    
  844.     {
    
  845.       // Valid because it's not an effect.
    
  846.       code: normalizeIndent`
    
  847.         function useMyThing(myRef) {
    
  848.           useCallback(() => {
    
  849.             const handleMouse = () => {};
    
  850.             myRef.current.addEventListener('mousemove', handleMouse);
    
  851.             myRef.current.addEventListener('mousein', handleMouse);
    
  852.             return function() {
    
  853.               setTimeout(() => {
    
  854.                 myRef.current.removeEventListener('mousemove', handleMouse);
    
  855.                 myRef.current.removeEventListener('mousein', handleMouse);
    
  856.               });
    
  857.             }
    
  858.           }, [myRef]);
    
  859.         }
    
  860.       `,
    
  861.     },
    
  862.     {
    
  863.       // Valid because we read ref.current in a function that isn't cleanup.
    
  864.       code: normalizeIndent`
    
  865.         function useMyThing() {
    
  866.           const myRef = useRef();
    
  867.           useEffect(() => {
    
  868.             const handleMove = () => {
    
  869.               console.log(myRef.current)
    
  870.             };
    
  871.             window.addEventListener('mousemove', handleMove);
    
  872.             return () => window.removeEventListener('mousemove', handleMove);
    
  873.           }, []);
    
  874.           return <div ref={myRef} />;
    
  875.         }
    
  876.       `,
    
  877.     },
    
  878.     {
    
  879.       // Valid because we read ref.current in a function that isn't cleanup.
    
  880.       code: normalizeIndent`
    
  881.         function useMyThing() {
    
  882.           const myRef = useRef();
    
  883.           useEffect(() => {
    
  884.             const handleMove = () => {
    
  885.               return () => window.removeEventListener('mousemove', handleMove);
    
  886.             };
    
  887.             window.addEventListener('mousemove', handleMove);
    
  888.             return () => {};
    
  889.           }, []);
    
  890.           return <div ref={myRef} />;
    
  891.         }
    
  892.       `,
    
  893.     },
    
  894.     {
    
  895.       // Valid because it's a primitive constant.
    
  896.       code: normalizeIndent`
    
  897.         function MyComponent() {
    
  898.           const local1 = 42;
    
  899.           const local2 = '42';
    
  900.           const local3 = null;
    
  901.           useEffect(() => {
    
  902.             console.log(local1);
    
  903.             console.log(local2);
    
  904.             console.log(local3);
    
  905.           }, []);
    
  906.         }
    
  907.       `,
    
  908.     },
    
  909.     {
    
  910.       // It's not a mistake to specify constant values though.
    
  911.       code: normalizeIndent`
    
  912.         function MyComponent() {
    
  913.           const local1 = 42;
    
  914.           const local2 = '42';
    
  915.           const local3 = null;
    
  916.           useEffect(() => {
    
  917.             console.log(local1);
    
  918.             console.log(local2);
    
  919.             console.log(local3);
    
  920.           }, [local1, local2, local3]);
    
  921.         }
    
  922.       `,
    
  923.     },
    
  924.     {
    
  925.       // It is valid for effects to over-specify their deps.
    
  926.       code: normalizeIndent`
    
  927.         function MyComponent(props) {
    
  928.           const local = props.local;
    
  929.           useEffect(() => {}, [local]);
    
  930.         }
    
  931.       `,
    
  932.     },
    
  933.     {
    
  934.       // Valid even though activeTab is "unused".
    
  935.       // We allow over-specifying deps for effects, but not callbacks or memo.
    
  936.       code: normalizeIndent`
    
  937.         function Foo({ activeTab }) {
    
  938.           useEffect(() => {
    
  939.             window.scrollTo(0, 0);
    
  940.           }, [activeTab]);
    
  941.         }
    
  942.       `,
    
  943.     },
    
  944.     {
    
  945.       // It is valid to specify broader effect deps than strictly necessary.
    
  946.       // Don't warn for this.
    
  947.       code: normalizeIndent`
    
  948.         function MyComponent(props) {
    
  949.           useEffect(() => {
    
  950.             console.log(props.foo.bar.baz);
    
  951.           }, [props]);
    
  952.           useEffect(() => {
    
  953.             console.log(props.foo.bar.baz);
    
  954.           }, [props.foo]);
    
  955.           useEffect(() => {
    
  956.             console.log(props.foo.bar.baz);
    
  957.           }, [props.foo.bar]);
    
  958.           useEffect(() => {
    
  959.             console.log(props.foo.bar.baz);
    
  960.           }, [props.foo.bar.baz]);
    
  961.         }
    
  962.       `,
    
  963.     },
    
  964.     {
    
  965.       // It is *also* valid to specify broader memo/callback deps than strictly necessary.
    
  966.       // Don't warn for this either.
    
  967.       code: normalizeIndent`
    
  968.         function MyComponent(props) {
    
  969.           const fn = useCallback(() => {
    
  970.             console.log(props.foo.bar.baz);
    
  971.           }, [props]);
    
  972.           const fn2 = useCallback(() => {
    
  973.             console.log(props.foo.bar.baz);
    
  974.           }, [props.foo]);
    
  975.           const fn3 = useMemo(() => {
    
  976.             console.log(props.foo.bar.baz);
    
  977.           }, [props.foo.bar]);
    
  978.           const fn4 = useMemo(() => {
    
  979.             console.log(props.foo.bar.baz);
    
  980.           }, [props.foo.bar.baz]);
    
  981.         }
    
  982.       `,
    
  983.     },
    
  984.     {
    
  985.       // Declaring handleNext is optional because
    
  986.       // it doesn't use anything in the function scope.
    
  987.       code: normalizeIndent`
    
  988.         function MyComponent(props) {
    
  989.           function handleNext1() {
    
  990.             console.log('hello');
    
  991.           }
    
  992.           const handleNext2 = () => {
    
  993.             console.log('hello');
    
  994.           };
    
  995.           let handleNext3 = function() {
    
  996.             console.log('hello');
    
  997.           };
    
  998.           useEffect(() => {
    
  999.             return Store.subscribe(handleNext1);
    
  1000.           }, []);
    
  1001.           useLayoutEffect(() => {
    
  1002.             return Store.subscribe(handleNext2);
    
  1003.           }, []);
    
  1004.           useMemo(() => {
    
  1005.             return Store.subscribe(handleNext3);
    
  1006.           }, []);
    
  1007.         }
    
  1008.       `,
    
  1009.     },
    
  1010.     {
    
  1011.       // Declaring handleNext is optional because
    
  1012.       // it doesn't use anything in the function scope.
    
  1013.       code: normalizeIndent`
    
  1014.         function MyComponent(props) {
    
  1015.           function handleNext() {
    
  1016.             console.log('hello');
    
  1017.           }
    
  1018.           useEffect(() => {
    
  1019.             return Store.subscribe(handleNext);
    
  1020.           }, []);
    
  1021.           useLayoutEffect(() => {
    
  1022.             return Store.subscribe(handleNext);
    
  1023.           }, []);
    
  1024.           useMemo(() => {
    
  1025.             return Store.subscribe(handleNext);
    
  1026.           }, []);
    
  1027.         }
    
  1028.       `,
    
  1029.     },
    
  1030.     {
    
  1031.       // Declaring handleNext is optional because
    
  1032.       // everything they use is fully static.
    
  1033.       code: normalizeIndent`
    
  1034.         function MyComponent(props) {
    
  1035.           let [, setState] = useState();
    
  1036.           let [, dispatch] = React.useReducer();
    
  1037. 
    
  1038.           function handleNext1(value) {
    
  1039.             let value2 = value * 100;
    
  1040.             setState(value2);
    
  1041.             console.log('hello');
    
  1042.           }
    
  1043.           const handleNext2 = (value) => {
    
  1044.             setState(foo(value));
    
  1045.             console.log('hello');
    
  1046.           };
    
  1047.           let handleNext3 = function(value) {
    
  1048.             console.log(value);
    
  1049.             dispatch({ type: 'x', value });
    
  1050.           };
    
  1051.           useEffect(() => {
    
  1052.             return Store.subscribe(handleNext1);
    
  1053.           }, []);
    
  1054.           useLayoutEffect(() => {
    
  1055.             return Store.subscribe(handleNext2);
    
  1056.           }, []);
    
  1057.           useMemo(() => {
    
  1058.             return Store.subscribe(handleNext3);
    
  1059.           }, []);
    
  1060.         }
    
  1061.       `,
    
  1062.     },
    
  1063.     {
    
  1064.       code: normalizeIndent`
    
  1065.         function useInterval(callback, delay) {
    
  1066.           const savedCallback = useRef();
    
  1067.           useEffect(() => {
    
  1068.             savedCallback.current = callback;
    
  1069.           });
    
  1070.           useEffect(() => {
    
  1071.             function tick() {
    
  1072.               savedCallback.current();
    
  1073.             }
    
  1074.             if (delay !== null) {
    
  1075.               let id = setInterval(tick, delay);
    
  1076.               return () => clearInterval(id);
    
  1077.             }
    
  1078.           }, [delay]);
    
  1079.         }
    
  1080.       `,
    
  1081.     },
    
  1082.     {
    
  1083.       code: normalizeIndent`
    
  1084.         function Counter() {
    
  1085.           const [count, setCount] = useState(0);
    
  1086. 
    
  1087.           useEffect(() => {
    
  1088.             let id = setInterval(() => {
    
  1089.               setCount(c => c + 1);
    
  1090.             }, 1000);
    
  1091.             return () => clearInterval(id);
    
  1092.           }, []);
    
  1093. 
    
  1094.           return <h1>{count}</h1>;
    
  1095.         }
    
  1096.       `,
    
  1097.     },
    
  1098.     {
    
  1099.       code: normalizeIndent`
    
  1100.         function Counter(unstableProp) {
    
  1101.           let [count, setCount] = useState(0);
    
  1102.           setCount = unstableProp
    
  1103.           useEffect(() => {
    
  1104.             let id = setInterval(() => {
    
  1105.               setCount(c => c + 1);
    
  1106.             }, 1000);
    
  1107.             return () => clearInterval(id);
    
  1108.           }, [setCount]);
    
  1109. 
    
  1110.           return <h1>{count}</h1>;
    
  1111.         }
    
  1112.       `,
    
  1113.     },
    
  1114.     {
    
  1115.       code: normalizeIndent`
    
  1116.         function Counter() {
    
  1117.           const [count, setCount] = useState(0);
    
  1118. 
    
  1119.           function tick() {
    
  1120.             setCount(c => c + 1);
    
  1121.           }
    
  1122. 
    
  1123.           useEffect(() => {
    
  1124.             let id = setInterval(() => {
    
  1125.               tick();
    
  1126.             }, 1000);
    
  1127.             return () => clearInterval(id);
    
  1128.           }, []);
    
  1129. 
    
  1130.           return <h1>{count}</h1>;
    
  1131.         }
    
  1132.       `,
    
  1133.     },
    
  1134.     {
    
  1135.       code: normalizeIndent`
    
  1136.         function Counter() {
    
  1137.           const [count, dispatch] = useReducer((state, action) => {
    
  1138.             if (action === 'inc') {
    
  1139.               return state + 1;
    
  1140.             }
    
  1141.           }, 0);
    
  1142. 
    
  1143.           useEffect(() => {
    
  1144.             let id = setInterval(() => {
    
  1145.               dispatch('inc');
    
  1146.             }, 1000);
    
  1147.             return () => clearInterval(id);
    
  1148.           }, []);
    
  1149. 
    
  1150.           return <h1>{count}</h1>;
    
  1151.         }
    
  1152.       `,
    
  1153.     },
    
  1154.     {
    
  1155.       code: normalizeIndent`
    
  1156.         function Counter() {
    
  1157.           const [count, dispatch] = useReducer((state, action) => {
    
  1158.             if (action === 'inc') {
    
  1159.               return state + 1;
    
  1160.             }
    
  1161.           }, 0);
    
  1162. 
    
  1163.           const tick = () => {
    
  1164.             dispatch('inc');
    
  1165.           };
    
  1166. 
    
  1167.           useEffect(() => {
    
  1168.             let id = setInterval(tick, 1000);
    
  1169.             return () => clearInterval(id);
    
  1170.           }, []);
    
  1171. 
    
  1172.           return <h1>{count}</h1>;
    
  1173.         }
    
  1174.       `,
    
  1175.     },
    
  1176.     {
    
  1177.       // Regression test for a crash
    
  1178.       code: normalizeIndent`
    
  1179.         function Podcasts() {
    
  1180.           useEffect(() => {
    
  1181.             setPodcasts([]);
    
  1182.           }, []);
    
  1183.           let [podcasts, setPodcasts] = useState(null);
    
  1184.         }
    
  1185.       `,
    
  1186.     },
    
  1187.     {
    
  1188.       code: normalizeIndent`
    
  1189.         function withFetch(fetchPodcasts) {
    
  1190.           return function Podcasts({ id }) {
    
  1191.             let [podcasts, setPodcasts] = useState(null);
    
  1192.             useEffect(() => {
    
  1193.               fetchPodcasts(id).then(setPodcasts);
    
  1194.             }, [id]);
    
  1195.           }
    
  1196.         }
    
  1197.       `,
    
  1198.     },
    
  1199.     {
    
  1200.       code: normalizeIndent`
    
  1201.         function Podcasts({ id }) {
    
  1202.           let [podcasts, setPodcasts] = useState(null);
    
  1203.           useEffect(() => {
    
  1204.             function doFetch({ fetchPodcasts }) {
    
  1205.               fetchPodcasts(id).then(setPodcasts);
    
  1206.             }
    
  1207.             doFetch({ fetchPodcasts: API.fetchPodcasts });
    
  1208.           }, [id]);
    
  1209.         }
    
  1210.       `,
    
  1211.     },
    
  1212.     {
    
  1213.       code: normalizeIndent`
    
  1214.         function Counter() {
    
  1215.           let [count, setCount] = useState(0);
    
  1216. 
    
  1217.           function increment(x) {
    
  1218.             return x + 1;
    
  1219.           }
    
  1220. 
    
  1221.           useEffect(() => {
    
  1222.             let id = setInterval(() => {
    
  1223.               setCount(increment);
    
  1224.             }, 1000);
    
  1225.             return () => clearInterval(id);
    
  1226.           }, []);
    
  1227. 
    
  1228.           return <h1>{count}</h1>;
    
  1229.         }
    
  1230.       `,
    
  1231.     },
    
  1232.     {
    
  1233.       code: normalizeIndent`
    
  1234.         function Counter() {
    
  1235.           let [count, setCount] = useState(0);
    
  1236. 
    
  1237.           function increment(x) {
    
  1238.             return x + 1;
    
  1239.           }
    
  1240. 
    
  1241.           useEffect(() => {
    
  1242.             let id = setInterval(() => {
    
  1243.               setCount(count => increment(count));
    
  1244.             }, 1000);
    
  1245.             return () => clearInterval(id);
    
  1246.           }, []);
    
  1247. 
    
  1248.           return <h1>{count}</h1>;
    
  1249.         }
    
  1250.       `,
    
  1251.     },
    
  1252.     {
    
  1253.       code: normalizeIndent`
    
  1254.         import increment from './increment';
    
  1255.         function Counter() {
    
  1256.           let [count, setCount] = useState(0);
    
  1257. 
    
  1258.           useEffect(() => {
    
  1259.             let id = setInterval(() => {
    
  1260.               setCount(count => count + increment);
    
  1261.             }, 1000);
    
  1262.             return () => clearInterval(id);
    
  1263.           }, []);
    
  1264. 
    
  1265.           return <h1>{count}</h1>;
    
  1266.         }
    
  1267.       `,
    
  1268.     },
    
  1269.     {
    
  1270.       code: normalizeIndent`
    
  1271.         function withStuff(increment) {
    
  1272.           return function Counter() {
    
  1273.             let [count, setCount] = useState(0);
    
  1274. 
    
  1275.             useEffect(() => {
    
  1276.               let id = setInterval(() => {
    
  1277.                 setCount(count => count + increment);
    
  1278.               }, 1000);
    
  1279.               return () => clearInterval(id);
    
  1280.             }, []);
    
  1281. 
    
  1282.             return <h1>{count}</h1>;
    
  1283.           }
    
  1284.         }
    
  1285.       `,
    
  1286.     },
    
  1287.     {
    
  1288.       code: normalizeIndent`
    
  1289.         function App() {
    
  1290.           const [query, setQuery] = useState('react');
    
  1291.           const [state, setState] = useState(null);
    
  1292.           useEffect(() => {
    
  1293.             let ignore = false;
    
  1294.             fetchSomething();
    
  1295.             async function fetchSomething() {
    
  1296.               const result = await (await fetch('http://hn.algolia.com/api/v1/search?query=' + query)).json();
    
  1297.               if (!ignore) setState(result);
    
  1298.             }
    
  1299.             return () => { ignore = true; };
    
  1300.           }, [query]);
    
  1301.           return (
    
  1302.             <>
    
  1303.               <input value={query} onChange={e => setQuery(e.target.value)} />
    
  1304.               {JSON.stringify(state)}
    
  1305.             </>
    
  1306.           );
    
  1307.         }
    
  1308.       `,
    
  1309.     },
    
  1310.     {
    
  1311.       code: normalizeIndent`
    
  1312.         function Example() {
    
  1313.           const foo = useCallback(() => {
    
  1314.             foo();
    
  1315.           }, []);
    
  1316.         }
    
  1317.       `,
    
  1318.     },
    
  1319.     {
    
  1320.       code: normalizeIndent`
    
  1321.         function Example({ prop }) {
    
  1322.           const foo = useCallback(() => {
    
  1323.             if (prop) {
    
  1324.               foo();
    
  1325.             }
    
  1326.           }, [prop]);
    
  1327.         }
    
  1328.       `,
    
  1329.     },
    
  1330.     {
    
  1331.       code: normalizeIndent`
    
  1332.         function Hello() {
    
  1333.           const [state, setState] = useState(0);
    
  1334.           useEffect(() => {
    
  1335.             const handleResize = () => setState(window.innerWidth);
    
  1336.             window.addEventListener('resize', handleResize);
    
  1337.             return () => window.removeEventListener('resize', handleResize);
    
  1338.           });
    
  1339.         }
    
  1340.       `,
    
  1341.     },
    
  1342.     // Ignore arguments keyword for arrow functions.
    
  1343.     {
    
  1344.       code: normalizeIndent`
    
  1345.         function Example() {
    
  1346.           useEffect(() => {
    
  1347.             arguments
    
  1348.           }, [])
    
  1349.         }
    
  1350.       `,
    
  1351.     },
    
  1352.     {
    
  1353.       code: normalizeIndent`
    
  1354.         function Example() {
    
  1355.           useEffect(() => {
    
  1356.             const bar = () => {
    
  1357.               arguments;
    
  1358.             };
    
  1359.             bar();
    
  1360.           }, [])
    
  1361.         }
    
  1362.       `,
    
  1363.     },
    
  1364.     // Regression test.
    
  1365.     {
    
  1366.       code: normalizeIndent`
    
  1367.         function Example(props) {
    
  1368.           useEffect(() => {
    
  1369.             let topHeight = 0;
    
  1370.             topHeight = props.upperViewHeight;
    
  1371.           }, [props.upperViewHeight]);
    
  1372.         }
    
  1373.       `,
    
  1374.     },
    
  1375.     // Regression test.
    
  1376.     {
    
  1377.       code: normalizeIndent`
    
  1378.         function Example(props) {
    
  1379.           useEffect(() => {
    
  1380.             let topHeight = 0;
    
  1381.             topHeight = props?.upperViewHeight;
    
  1382.           }, [props?.upperViewHeight]);
    
  1383.         }
    
  1384.       `,
    
  1385.     },
    
  1386.     // Regression test.
    
  1387.     {
    
  1388.       code: normalizeIndent`
    
  1389.         function Example(props) {
    
  1390.           useEffect(() => {
    
  1391.             let topHeight = 0;
    
  1392.             topHeight = props?.upperViewHeight;
    
  1393.           }, [props]);
    
  1394.         }
    
  1395.       `,
    
  1396.     },
    
  1397.     {
    
  1398.       code: normalizeIndent`
    
  1399.         function useFoo(foo){
    
  1400.           return useMemo(() => foo, [foo]);
    
  1401.         }
    
  1402.       `,
    
  1403.     },
    
  1404.     {
    
  1405.       code: normalizeIndent`
    
  1406.         function useFoo(){
    
  1407.           const foo = "hi!";
    
  1408.           return useMemo(() => foo, [foo]);
    
  1409.         }
    
  1410.       `,
    
  1411.     },
    
  1412.     {
    
  1413.       code: normalizeIndent`
    
  1414.         function useFoo(){
    
  1415.           let {foo} = {foo: 1};
    
  1416.           return useMemo(() => foo, [foo]);
    
  1417.         }
    
  1418.       `,
    
  1419.     },
    
  1420.     {
    
  1421.       code: normalizeIndent`
    
  1422.         function useFoo(){
    
  1423.           let [foo] = [1];
    
  1424.           return useMemo(() => foo, [foo]);
    
  1425.         }
    
  1426.       `,
    
  1427.     },
    
  1428.     {
    
  1429.       code: normalizeIndent`
    
  1430.         function useFoo() {
    
  1431.           const foo = "fine";
    
  1432.           if (true) {
    
  1433.             // Shadowed variable with constant construction in a nested scope is fine.
    
  1434.             const foo = {};
    
  1435.           }
    
  1436.           return useMemo(() => foo, [foo]);
    
  1437.         }
    
  1438.       `,
    
  1439.     },
    
  1440.     {
    
  1441.       code: normalizeIndent`
    
  1442.         function MyComponent({foo}) {
    
  1443.           return useMemo(() => foo, [foo])
    
  1444.         }
    
  1445.       `,
    
  1446.     },
    
  1447.     {
    
  1448.       code: normalizeIndent`
    
  1449.         function MyComponent() {
    
  1450.           const foo = true ? "fine" : "also fine";
    
  1451.           return useMemo(() => foo, [foo]);
    
  1452.         }
    
  1453.       `,
    
  1454.     },
    
  1455.     {
    
  1456.       code: normalizeIndent`
    
  1457.         function MyComponent() {
    
  1458.           useEffect(() => {
    
  1459.             console.log('banana banana banana');
    
  1460.           }, undefined);
    
  1461.         }
    
  1462.       `,
    
  1463.     },
    
  1464.   ],
    
  1465.   invalid: [
    
  1466.     {
    
  1467.       code: normalizeIndent`
    
  1468.         function MyComponent(props) {
    
  1469.           useCallback(() => {
    
  1470.             console.log(props.foo?.toString());
    
  1471.           }, []);
    
  1472.         }
    
  1473.       `,
    
  1474.       errors: [
    
  1475.         {
    
  1476.           message:
    
  1477.             "React Hook useCallback has a missing dependency: 'props.foo'. " +
    
  1478.             'Either include it or remove the dependency array.',
    
  1479.           suggestions: [
    
  1480.             {
    
  1481.               desc: 'Update the dependencies array to be: [props.foo]',
    
  1482.               output: normalizeIndent`
    
  1483.                 function MyComponent(props) {
    
  1484.                   useCallback(() => {
    
  1485.                     console.log(props.foo?.toString());
    
  1486.                   }, [props.foo]);
    
  1487.                 }
    
  1488.               `,
    
  1489.             },
    
  1490.           ],
    
  1491.         },
    
  1492.       ],
    
  1493.     },
    
  1494.     {
    
  1495.       code: normalizeIndent`
    
  1496.         function MyComponent(props) {
    
  1497.           useCallback(() => {
    
  1498.             console.log(props.foo?.bar.baz);
    
  1499.           }, []);
    
  1500.         }
    
  1501.       `,
    
  1502.       errors: [
    
  1503.         {
    
  1504.           message:
    
  1505.             "React Hook useCallback has a missing dependency: 'props.foo?.bar.baz'. " +
    
  1506.             'Either include it or remove the dependency array.',
    
  1507.           suggestions: [
    
  1508.             {
    
  1509.               desc: 'Update the dependencies array to be: [props.foo?.bar.baz]',
    
  1510.               output: normalizeIndent`
    
  1511.                 function MyComponent(props) {
    
  1512.                   useCallback(() => {
    
  1513.                     console.log(props.foo?.bar.baz);
    
  1514.                   }, [props.foo?.bar.baz]);
    
  1515.                 }
    
  1516.               `,
    
  1517.             },
    
  1518.           ],
    
  1519.         },
    
  1520.       ],
    
  1521.     },
    
  1522.     {
    
  1523.       code: normalizeIndent`
    
  1524.         function MyComponent(props) {
    
  1525.           useCallback(() => {
    
  1526.             console.log(props.foo?.bar?.baz);
    
  1527.           }, []);
    
  1528.         }
    
  1529.       `,
    
  1530.       errors: [
    
  1531.         {
    
  1532.           message:
    
  1533.             "React Hook useCallback has a missing dependency: 'props.foo?.bar?.baz'. " +
    
  1534.             'Either include it or remove the dependency array.',
    
  1535.           suggestions: [
    
  1536.             {
    
  1537.               desc: 'Update the dependencies array to be: [props.foo?.bar?.baz]',
    
  1538.               output: normalizeIndent`
    
  1539.                 function MyComponent(props) {
    
  1540.                   useCallback(() => {
    
  1541.                     console.log(props.foo?.bar?.baz);
    
  1542.                   }, [props.foo?.bar?.baz]);
    
  1543.                 }
    
  1544.               `,
    
  1545.             },
    
  1546.           ],
    
  1547.         },
    
  1548.       ],
    
  1549.     },
    
  1550.     {
    
  1551.       code: normalizeIndent`
    
  1552.         function MyComponent(props) {
    
  1553.           useCallback(() => {
    
  1554.             console.log(props.foo?.bar.toString());
    
  1555.           }, []);
    
  1556.         }
    
  1557.       `,
    
  1558.       errors: [
    
  1559.         {
    
  1560.           message:
    
  1561.             "React Hook useCallback has a missing dependency: 'props.foo?.bar'. " +
    
  1562.             'Either include it or remove the dependency array.',
    
  1563.           suggestions: [
    
  1564.             {
    
  1565.               desc: 'Update the dependencies array to be: [props.foo?.bar]',
    
  1566.               output: normalizeIndent`
    
  1567.                 function MyComponent(props) {
    
  1568.                   useCallback(() => {
    
  1569.                     console.log(props.foo?.bar.toString());
    
  1570.                   }, [props.foo?.bar]);
    
  1571.                 }
    
  1572.               `,
    
  1573.             },
    
  1574.           ],
    
  1575.         },
    
  1576.       ],
    
  1577.     },
    
  1578.     {
    
  1579.       code: normalizeIndent`
    
  1580.         function MyComponent() {
    
  1581.           const local = someFunc();
    
  1582.           useEffect(() => {
    
  1583.             console.log(local);
    
  1584.           }, []);
    
  1585.         }
    
  1586.       `,
    
  1587.       errors: [
    
  1588.         {
    
  1589.           message:
    
  1590.             "React Hook useEffect has a missing dependency: 'local'. " +
    
  1591.             'Either include it or remove the dependency array.',
    
  1592.           suggestions: [
    
  1593.             {
    
  1594.               desc: 'Update the dependencies array to be: [local]',
    
  1595.               output: normalizeIndent`
    
  1596.                 function MyComponent() {
    
  1597.                   const local = someFunc();
    
  1598.                   useEffect(() => {
    
  1599.                     console.log(local);
    
  1600.                   }, [local]);
    
  1601.                 }
    
  1602.               `,
    
  1603.             },
    
  1604.           ],
    
  1605.         },
    
  1606.       ],
    
  1607.     },
    
  1608.     {
    
  1609.       code: normalizeIndent`
    
  1610.         function Counter(unstableProp) {
    
  1611.           let [count, setCount] = useState(0);
    
  1612.           setCount = unstableProp
    
  1613.           useEffect(() => {
    
  1614.             let id = setInterval(() => {
    
  1615.               setCount(c => c + 1);
    
  1616.             }, 1000);
    
  1617.             return () => clearInterval(id);
    
  1618.           }, []);
    
  1619. 
    
  1620.           return <h1>{count}</h1>;
    
  1621.         }
    
  1622.       `,
    
  1623.       errors: [
    
  1624.         {
    
  1625.           message:
    
  1626.             "React Hook useEffect has a missing dependency: 'setCount'. " +
    
  1627.             'Either include it or remove the dependency array.',
    
  1628.           suggestions: [
    
  1629.             {
    
  1630.               desc: 'Update the dependencies array to be: [setCount]',
    
  1631.               output: normalizeIndent`
    
  1632.                 function Counter(unstableProp) {
    
  1633.                   let [count, setCount] = useState(0);
    
  1634.                   setCount = unstableProp
    
  1635.                   useEffect(() => {
    
  1636.                     let id = setInterval(() => {
    
  1637.                       setCount(c => c + 1);
    
  1638.                     }, 1000);
    
  1639.                     return () => clearInterval(id);
    
  1640.                   }, [setCount]);
    
  1641. 
    
  1642.                   return <h1>{count}</h1>;
    
  1643.                 }
    
  1644.               `,
    
  1645.             },
    
  1646.           ],
    
  1647.         },
    
  1648.       ],
    
  1649.     },
    
  1650.     {
    
  1651.       // Note: we *could* detect it's a primitive and never assigned
    
  1652.       // even though it's not a constant -- but we currently don't.
    
  1653.       // So this is an error.
    
  1654.       code: normalizeIndent`
    
  1655.         function MyComponent() {
    
  1656.           let local = 42;
    
  1657.           useEffect(() => {
    
  1658.             console.log(local);
    
  1659.           }, []);
    
  1660.         }
    
  1661.       `,
    
  1662.       errors: [
    
  1663.         {
    
  1664.           message:
    
  1665.             "React Hook useEffect has a missing dependency: 'local'. " +
    
  1666.             'Either include it or remove the dependency array.',
    
  1667.           suggestions: [
    
  1668.             {
    
  1669.               desc: 'Update the dependencies array to be: [local]',
    
  1670.               output: normalizeIndent`
    
  1671.                 function MyComponent() {
    
  1672.                   let local = 42;
    
  1673.                   useEffect(() => {
    
  1674.                     console.log(local);
    
  1675.                   }, [local]);
    
  1676.                 }
    
  1677.               `,
    
  1678.             },
    
  1679.           ],
    
  1680.         },
    
  1681.       ],
    
  1682.     },
    
  1683.     {
    
  1684.       // Regexes are literals but potentially stateful.
    
  1685.       code: normalizeIndent`
    
  1686.         function MyComponent() {
    
  1687.           const local = /foo/;
    
  1688.           useEffect(() => {
    
  1689.             console.log(local);
    
  1690.           }, []);
    
  1691.         }
    
  1692.       `,
    
  1693.       errors: [
    
  1694.         {
    
  1695.           message:
    
  1696.             "React Hook useEffect has a missing dependency: 'local'. " +
    
  1697.             'Either include it or remove the dependency array.',
    
  1698.           suggestions: [
    
  1699.             {
    
  1700.               desc: 'Update the dependencies array to be: [local]',
    
  1701.               output: normalizeIndent`
    
  1702.                 function MyComponent() {
    
  1703.                   const local = /foo/;
    
  1704.                   useEffect(() => {
    
  1705.                     console.log(local);
    
  1706.                   }, [local]);
    
  1707.                 }
    
  1708.               `,
    
  1709.             },
    
  1710.           ],
    
  1711.         },
    
  1712.       ],
    
  1713.     },
    
  1714.     {
    
  1715.       // Invalid because they don't have a meaning without deps.
    
  1716.       code: normalizeIndent`
    
  1717.         function MyComponent(props) {
    
  1718.           const value = useMemo(() => { return 2*2; });
    
  1719.           const fn = useCallback(() => { alert('foo'); });
    
  1720.         }
    
  1721.       `,
    
  1722.       // We don't know what you meant.
    
  1723.       errors: [
    
  1724.         {
    
  1725.           message:
    
  1726.             'React Hook useMemo does nothing when called with only one argument. ' +
    
  1727.             'Did you forget to pass an array of dependencies?',
    
  1728.           suggestions: undefined,
    
  1729.         },
    
  1730.         {
    
  1731.           message:
    
  1732.             'React Hook useCallback does nothing when called with only one argument. ' +
    
  1733.             'Did you forget to pass an array of dependencies?',
    
  1734.           suggestions: undefined,
    
  1735.         },
    
  1736.       ],
    
  1737.     },
    
  1738.     {
    
  1739.       // Invalid because they don't have a meaning without deps.
    
  1740.       code: normalizeIndent`
    
  1741.         function MyComponent({ fn1, fn2 }) {
    
  1742.           const value = useMemo(fn1);
    
  1743.           const fn = useCallback(fn2);
    
  1744.         }
    
  1745.       `,
    
  1746.       errors: [
    
  1747.         {
    
  1748.           message:
    
  1749.             'React Hook useMemo does nothing when called with only one argument. ' +
    
  1750.             'Did you forget to pass an array of dependencies?',
    
  1751.           suggestions: undefined,
    
  1752.         },
    
  1753.         {
    
  1754.           message:
    
  1755.             'React Hook useCallback does nothing when called with only one argument. ' +
    
  1756.             'Did you forget to pass an array of dependencies?',
    
  1757.           suggestions: undefined,
    
  1758.         },
    
  1759.       ],
    
  1760.     },
    
  1761.     {
    
  1762.       code: normalizeIndent`
    
  1763.         function MyComponent() {
    
  1764.           useEffect()
    
  1765.           useLayoutEffect()
    
  1766.           useCallback()
    
  1767.           useMemo()
    
  1768.         }
    
  1769.       `,
    
  1770.       errors: [
    
  1771.         {
    
  1772.           message:
    
  1773.             'React Hook useEffect requires an effect callback. ' +
    
  1774.             'Did you forget to pass a callback to the hook?',
    
  1775.           suggestions: undefined,
    
  1776.         },
    
  1777.         {
    
  1778.           message:
    
  1779.             'React Hook useLayoutEffect requires an effect callback. ' +
    
  1780.             'Did you forget to pass a callback to the hook?',
    
  1781.           suggestions: undefined,
    
  1782.         },
    
  1783.         {
    
  1784.           message:
    
  1785.             'React Hook useCallback requires an effect callback. ' +
    
  1786.             'Did you forget to pass a callback to the hook?',
    
  1787.           suggestions: undefined,
    
  1788.         },
    
  1789.         {
    
  1790.           message:
    
  1791.             'React Hook useMemo requires an effect callback. ' +
    
  1792.             'Did you forget to pass a callback to the hook?',
    
  1793.           suggestions: undefined,
    
  1794.         },
    
  1795.       ],
    
  1796.     },
    
  1797.     {
    
  1798.       // Regression test
    
  1799.       code: normalizeIndent`
    
  1800.         function MyComponent() {
    
  1801.           const local = someFunc();
    
  1802.           useEffect(() => {
    
  1803.             if (true) {
    
  1804.               console.log(local);
    
  1805.             }
    
  1806.           }, []);
    
  1807.         }
    
  1808.       `,
    
  1809.       errors: [
    
  1810.         {
    
  1811.           message:
    
  1812.             "React Hook useEffect has a missing dependency: 'local'. " +
    
  1813.             'Either include it or remove the dependency array.',
    
  1814.           suggestions: [
    
  1815.             {
    
  1816.               desc: 'Update the dependencies array to be: [local]',
    
  1817.               output: normalizeIndent`
    
  1818.                 function MyComponent() {
    
  1819.                   const local = someFunc();
    
  1820.                   useEffect(() => {
    
  1821.                     if (true) {
    
  1822.                       console.log(local);
    
  1823.                     }
    
  1824.                   }, [local]);
    
  1825.                 }
    
  1826.               `,
    
  1827.             },
    
  1828.           ],
    
  1829.         },
    
  1830.       ],
    
  1831.     },
    
  1832.     {
    
  1833.       // Regression test
    
  1834.       code: normalizeIndent`
    
  1835.         function MyComponent() {
    
  1836.           const local = {};
    
  1837.           useEffect(() => {
    
  1838.             try {
    
  1839.               console.log(local);
    
  1840.             } finally {}
    
  1841.           }, []);
    
  1842.         }
    
  1843.       `,
    
  1844.       errors: [
    
  1845.         {
    
  1846.           message:
    
  1847.             "React Hook useEffect has a missing dependency: 'local'. " +
    
  1848.             'Either include it or remove the dependency array.',
    
  1849.           suggestions: [
    
  1850.             {
    
  1851.               desc: 'Update the dependencies array to be: [local]',
    
  1852.               output: normalizeIndent`
    
  1853.                 function MyComponent() {
    
  1854.                   const local = {};
    
  1855.                   useEffect(() => {
    
  1856.                     try {
    
  1857.                       console.log(local);
    
  1858.                     } finally {}
    
  1859.                   }, [local]);
    
  1860.                 }
    
  1861.               `,
    
  1862.             },
    
  1863.           ],
    
  1864.         },
    
  1865.       ],
    
  1866.     },
    
  1867.     {
    
  1868.       // Regression test
    
  1869.       code: normalizeIndent`
    
  1870.         function MyComponent() {
    
  1871.           const local = {};
    
  1872.           useEffect(() => {
    
  1873.             function inner() {
    
  1874.               console.log(local);
    
  1875.             }
    
  1876.             inner();
    
  1877.           }, []);
    
  1878.         }
    
  1879.       `,
    
  1880.       errors: [
    
  1881.         {
    
  1882.           message:
    
  1883.             "React Hook useEffect has a missing dependency: 'local'. " +
    
  1884.             'Either include it or remove the dependency array.',
    
  1885.           suggestions: [
    
  1886.             {
    
  1887.               desc: 'Update the dependencies array to be: [local]',
    
  1888.               output: normalizeIndent`
    
  1889.                 function MyComponent() {
    
  1890.                   const local = {};
    
  1891.                   useEffect(() => {
    
  1892.                     function inner() {
    
  1893.                       console.log(local);
    
  1894.                     }
    
  1895.                     inner();
    
  1896.                   }, [local]);
    
  1897.                 }
    
  1898.               `,
    
  1899.             },
    
  1900.           ],
    
  1901.         },
    
  1902.       ],
    
  1903.     },
    
  1904.     {
    
  1905.       code: normalizeIndent`
    
  1906.         function MyComponent() {
    
  1907.           const local1 = someFunc();
    
  1908.           {
    
  1909.             const local2 = someFunc();
    
  1910.             useEffect(() => {
    
  1911.               console.log(local1);
    
  1912.               console.log(local2);
    
  1913.             }, []);
    
  1914.           }
    
  1915.         }
    
  1916.       `,
    
  1917.       errors: [
    
  1918.         {
    
  1919.           message:
    
  1920.             "React Hook useEffect has missing dependencies: 'local1' and 'local2'. " +
    
  1921.             'Either include them or remove the dependency array.',
    
  1922.           suggestions: [
    
  1923.             {
    
  1924.               desc: 'Update the dependencies array to be: [local1, local2]',
    
  1925.               output: normalizeIndent`
    
  1926.                 function MyComponent() {
    
  1927.                   const local1 = someFunc();
    
  1928.                   {
    
  1929.                     const local2 = someFunc();
    
  1930.                     useEffect(() => {
    
  1931.                       console.log(local1);
    
  1932.                       console.log(local2);
    
  1933.                     }, [local1, local2]);
    
  1934.                   }
    
  1935.                 }
    
  1936.               `,
    
  1937.             },
    
  1938.           ],
    
  1939.         },
    
  1940.       ],
    
  1941.     },
    
  1942.     {
    
  1943.       code: normalizeIndent`
    
  1944.         function MyComponent() {
    
  1945.           const local1 = {};
    
  1946.           const local2 = {};
    
  1947.           useEffect(() => {
    
  1948.             console.log(local1);
    
  1949.             console.log(local2);
    
  1950.           }, [local1]);
    
  1951.         }
    
  1952.       `,
    
  1953.       errors: [
    
  1954.         {
    
  1955.           message:
    
  1956.             "React Hook useEffect has a missing dependency: 'local2'. " +
    
  1957.             'Either include it or remove the dependency array.',
    
  1958.           suggestions: [
    
  1959.             {
    
  1960.               desc: 'Update the dependencies array to be: [local1, local2]',
    
  1961.               output: normalizeIndent`
    
  1962.                 function MyComponent() {
    
  1963.                   const local1 = {};
    
  1964.                   const local2 = {};
    
  1965.                   useEffect(() => {
    
  1966.                     console.log(local1);
    
  1967.                     console.log(local2);
    
  1968.                   }, [local1, local2]);
    
  1969.                 }
    
  1970.               `,
    
  1971.             },
    
  1972.           ],
    
  1973.         },
    
  1974.       ],
    
  1975.     },
    
  1976.     {
    
  1977.       code: normalizeIndent`
    
  1978.         function MyComponent() {
    
  1979.           const local1 = {};
    
  1980.           const local2 = {};
    
  1981.           useMemo(() => {
    
  1982.             console.log(local1);
    
  1983.           }, [local1, local2]);
    
  1984.         }
    
  1985.       `,
    
  1986.       errors: [
    
  1987.         {
    
  1988.           message:
    
  1989.             "React Hook useMemo has an unnecessary dependency: 'local2'. " +
    
  1990.             'Either exclude it or remove the dependency array.',
    
  1991.           suggestions: [
    
  1992.             {
    
  1993.               desc: 'Update the dependencies array to be: [local1]',
    
  1994.               output: normalizeIndent`
    
  1995.                 function MyComponent() {
    
  1996.                   const local1 = {};
    
  1997.                   const local2 = {};
    
  1998.                   useMemo(() => {
    
  1999.                     console.log(local1);
    
  2000.                   }, [local1]);
    
  2001.                 }
    
  2002.               `,
    
  2003.             },
    
  2004.           ],
    
  2005.         },
    
  2006.       ],
    
  2007.     },
    
  2008.     {
    
  2009.       code: normalizeIndent`
    
  2010.         function MyComponent() {
    
  2011.           const local1 = someFunc();
    
  2012.           function MyNestedComponent() {
    
  2013.             const local2 = {};
    
  2014.             useCallback(() => {
    
  2015.               console.log(local1);
    
  2016.               console.log(local2);
    
  2017.             }, [local1]);
    
  2018.           }
    
  2019.         }
    
  2020.       `,
    
  2021.       errors: [
    
  2022.         {
    
  2023.           message:
    
  2024.             "React Hook useCallback has a missing dependency: 'local2'. " +
    
  2025.             'Either include it or remove the dependency array. ' +
    
  2026.             "Outer scope values like 'local1' aren't valid dependencies " +
    
  2027.             "because mutating them doesn't re-render the component.",
    
  2028.           suggestions: [
    
  2029.             {
    
  2030.               desc: 'Update the dependencies array to be: [local2]',
    
  2031.               output: normalizeIndent`
    
  2032.                 function MyComponent() {
    
  2033.                   const local1 = someFunc();
    
  2034.                   function MyNestedComponent() {
    
  2035.                     const local2 = {};
    
  2036.                     useCallback(() => {
    
  2037.                       console.log(local1);
    
  2038.                       console.log(local2);
    
  2039.                     }, [local2]);
    
  2040.                   }
    
  2041.                 }
    
  2042.               `,
    
  2043.             },
    
  2044.           ],
    
  2045.         },
    
  2046.       ],
    
  2047.     },
    
  2048.     {
    
  2049.       code: normalizeIndent`
    
  2050.         function MyComponent() {
    
  2051.           const local = {};
    
  2052.           useEffect(() => {
    
  2053.             console.log(local);
    
  2054.             console.log(local);
    
  2055.           }, []);
    
  2056.         }
    
  2057.       `,
    
  2058.       errors: [
    
  2059.         {
    
  2060.           message:
    
  2061.             "React Hook useEffect has a missing dependency: 'local'. " +
    
  2062.             'Either include it or remove the dependency array.',
    
  2063.           suggestions: [
    
  2064.             {
    
  2065.               desc: 'Update the dependencies array to be: [local]',
    
  2066.               output: normalizeIndent`
    
  2067.                 function MyComponent() {
    
  2068.                   const local = {};
    
  2069.                   useEffect(() => {
    
  2070.                     console.log(local);
    
  2071.                     console.log(local);
    
  2072.                   }, [local]);
    
  2073.                 }
    
  2074.               `,
    
  2075.             },
    
  2076.           ],
    
  2077.         },
    
  2078.       ],
    
  2079.     },
    
  2080.     {
    
  2081.       code: normalizeIndent`
    
  2082.         function MyComponent() {
    
  2083.           const local = {};
    
  2084.           useEffect(() => {
    
  2085.             console.log(local);
    
  2086.             console.log(local);
    
  2087.           }, [local, local]);
    
  2088.         }
    
  2089.       `,
    
  2090.       errors: [
    
  2091.         {
    
  2092.           message:
    
  2093.             "React Hook useEffect has a duplicate dependency: 'local'. " +
    
  2094.             'Either omit it or remove the dependency array.',
    
  2095.           suggestions: [
    
  2096.             {
    
  2097.               desc: 'Update the dependencies array to be: [local]',
    
  2098.               output: normalizeIndent`
    
  2099.                 function MyComponent() {
    
  2100.                   const local = {};
    
  2101.                   useEffect(() => {
    
  2102.                     console.log(local);
    
  2103.                     console.log(local);
    
  2104.                   }, [local]);
    
  2105.                 }
    
  2106.               `,
    
  2107.             },
    
  2108.           ],
    
  2109.         },
    
  2110.       ],
    
  2111.     },
    
  2112.     {
    
  2113.       code: normalizeIndent`
    
  2114.         function MyComponent() {
    
  2115.           useCallback(() => {}, [window]);
    
  2116.         }
    
  2117.       `,
    
  2118.       errors: [
    
  2119.         {
    
  2120.           message:
    
  2121.             "React Hook useCallback has an unnecessary dependency: 'window'. " +
    
  2122.             'Either exclude it or remove the dependency array. ' +
    
  2123.             "Outer scope values like 'window' aren't valid dependencies " +
    
  2124.             "because mutating them doesn't re-render the component.",
    
  2125.           suggestions: [
    
  2126.             {
    
  2127.               desc: 'Update the dependencies array to be: []',
    
  2128.               output: normalizeIndent`
    
  2129.                 function MyComponent() {
    
  2130.                   useCallback(() => {}, []);
    
  2131.                 }
    
  2132.               `,
    
  2133.             },
    
  2134.           ],
    
  2135.         },
    
  2136.       ],
    
  2137.     },
    
  2138.     {
    
  2139.       // It is not valid for useCallback to specify extraneous deps
    
  2140.       // because it doesn't serve as a side effect trigger unlike useEffect.
    
  2141.       code: normalizeIndent`
    
  2142.         function MyComponent(props) {
    
  2143.           let local = props.foo;
    
  2144.           useCallback(() => {}, [local]);
    
  2145.         }
    
  2146.       `,
    
  2147.       errors: [
    
  2148.         {
    
  2149.           message:
    
  2150.             "React Hook useCallback has an unnecessary dependency: 'local'. " +
    
  2151.             'Either exclude it or remove the dependency array.',
    
  2152.           suggestions: [
    
  2153.             {
    
  2154.               desc: 'Update the dependencies array to be: []',
    
  2155.               output: normalizeIndent`
    
  2156.                 function MyComponent(props) {
    
  2157.                   let local = props.foo;
    
  2158.                   useCallback(() => {}, []);
    
  2159.                 }
    
  2160.               `,
    
  2161.             },
    
  2162.           ],
    
  2163.         },
    
  2164.       ],
    
  2165.     },
    
  2166.     {
    
  2167.       code: normalizeIndent`
    
  2168.         function MyComponent({ history }) {
    
  2169.           useEffect(() => {
    
  2170.             return history.listen();
    
  2171.           }, []);
    
  2172.         }
    
  2173.       `,
    
  2174.       errors: [
    
  2175.         {
    
  2176.           message:
    
  2177.             "React Hook useEffect has a missing dependency: 'history'. " +
    
  2178.             'Either include it or remove the dependency array.',
    
  2179.           suggestions: [
    
  2180.             {
    
  2181.               desc: 'Update the dependencies array to be: [history]',
    
  2182.               output: normalizeIndent`
    
  2183.                 function MyComponent({ history }) {
    
  2184.                   useEffect(() => {
    
  2185.                     return history.listen();
    
  2186.                   }, [history]);
    
  2187.                 }
    
  2188.               `,
    
  2189.             },
    
  2190.           ],
    
  2191.         },
    
  2192.       ],
    
  2193.     },
    
  2194.     {
    
  2195.       code: normalizeIndent`
    
  2196.         function MyComponent({ history }) {
    
  2197.           useEffect(() => {
    
  2198.             return [
    
  2199.               history.foo.bar[2].dobedo.listen(),
    
  2200.               history.foo.bar().dobedo.listen[2]
    
  2201.             ];
    
  2202.           }, []);
    
  2203.         }
    
  2204.       `,
    
  2205.       errors: [
    
  2206.         {
    
  2207.           message:
    
  2208.             "React Hook useEffect has a missing dependency: 'history.foo'. " +
    
  2209.             'Either include it or remove the dependency array.',
    
  2210.           suggestions: [
    
  2211.             {
    
  2212.               desc: 'Update the dependencies array to be: [history.foo]',
    
  2213.               output: normalizeIndent`
    
  2214.                 function MyComponent({ history }) {
    
  2215.                   useEffect(() => {
    
  2216.                     return [
    
  2217.                       history.foo.bar[2].dobedo.listen(),
    
  2218.                       history.foo.bar().dobedo.listen[2]
    
  2219.                     ];
    
  2220.                   }, [history.foo]);
    
  2221.                 }
    
  2222.               `,
    
  2223.             },
    
  2224.           ],
    
  2225.         },
    
  2226.       ],
    
  2227.     },
    
  2228.     {
    
  2229.       code: normalizeIndent`
    
  2230.         function MyComponent({ history }) {
    
  2231.           useEffect(() => {
    
  2232.             return [
    
  2233.               history?.foo
    
  2234.             ];
    
  2235.           }, []);
    
  2236.         }
    
  2237.       `,
    
  2238.       errors: [
    
  2239.         {
    
  2240.           message:
    
  2241.             "React Hook useEffect has a missing dependency: 'history?.foo'. " +
    
  2242.             'Either include it or remove the dependency array.',
    
  2243.           suggestions: [
    
  2244.             {
    
  2245.               desc: 'Update the dependencies array to be: [history?.foo]',
    
  2246.               output: normalizeIndent`
    
  2247.                 function MyComponent({ history }) {
    
  2248.                   useEffect(() => {
    
  2249.                     return [
    
  2250.                       history?.foo
    
  2251.                     ];
    
  2252.                   }, [history?.foo]);
    
  2253.                 }
    
  2254.               `,
    
  2255.             },
    
  2256.           ],
    
  2257.         },
    
  2258.       ],
    
  2259.     },
    
  2260.     {
    
  2261.       code: normalizeIndent`
    
  2262.         function MyComponent() {
    
  2263.           useEffect(() => {}, ['foo']);
    
  2264.         }
    
  2265.       `,
    
  2266.       errors: [
    
  2267.         {
    
  2268.           message:
    
  2269.             // Don't assume user meant `foo` because it's not used in the effect.
    
  2270.             "The 'foo' literal is not a valid dependency because it never changes. " +
    
  2271.             'You can safely remove it.',
    
  2272.           // TODO: provide suggestion.
    
  2273.           suggestions: undefined,
    
  2274.         },
    
  2275.       ],
    
  2276.     },
    
  2277.     {
    
  2278.       code: normalizeIndent`
    
  2279.         function MyComponent({ foo, bar, baz }) {
    
  2280.           useEffect(() => {
    
  2281.             console.log(foo, bar, baz);
    
  2282.           }, ['foo', 'bar']);
    
  2283.         }
    
  2284.       `,
    
  2285.       errors: [
    
  2286.         {
    
  2287.           message:
    
  2288.             "React Hook useEffect has missing dependencies: 'bar', 'baz', and 'foo'. " +
    
  2289.             'Either include them or remove the dependency array.',
    
  2290.           suggestions: [
    
  2291.             {
    
  2292.               desc: 'Update the dependencies array to be: [bar, baz, foo]',
    
  2293.               output: normalizeIndent`
    
  2294.                 function MyComponent({ foo, bar, baz }) {
    
  2295.                   useEffect(() => {
    
  2296.                     console.log(foo, bar, baz);
    
  2297.                   }, [bar, baz, foo]);
    
  2298.                 }
    
  2299.               `,
    
  2300.             },
    
  2301.           ],
    
  2302.         },
    
  2303.         {
    
  2304.           message:
    
  2305.             "The 'foo' literal is not a valid dependency because it never changes. " +
    
  2306.             'Did you mean to include foo in the array instead?',
    
  2307.           suggestions: undefined,
    
  2308.         },
    
  2309.         {
    
  2310.           message:
    
  2311.             "The 'bar' literal is not a valid dependency because it never changes. " +
    
  2312.             'Did you mean to include bar in the array instead?',
    
  2313.           suggestions: undefined,
    
  2314.         },
    
  2315.       ],
    
  2316.     },
    
  2317.     {
    
  2318.       code: normalizeIndent`
    
  2319.         function MyComponent({ foo, bar, baz }) {
    
  2320.           useEffect(() => {
    
  2321.             console.log(foo, bar, baz);
    
  2322.           }, [42, false, null]);
    
  2323.         }
    
  2324.       `,
    
  2325.       errors: [
    
  2326.         {
    
  2327.           message:
    
  2328.             "React Hook useEffect has missing dependencies: 'bar', 'baz', and 'foo'. " +
    
  2329.             'Either include them or remove the dependency array.',
    
  2330.           suggestions: [
    
  2331.             {
    
  2332.               desc: 'Update the dependencies array to be: [bar, baz, foo]',
    
  2333.               output: normalizeIndent`
    
  2334.                 function MyComponent({ foo, bar, baz }) {
    
  2335.                   useEffect(() => {
    
  2336.                     console.log(foo, bar, baz);
    
  2337.                   }, [bar, baz, foo]);
    
  2338.                 }
    
  2339.               `,
    
  2340.             },
    
  2341.           ],
    
  2342.         },
    
  2343.         {
    
  2344.           message:
    
  2345.             'The 42 literal is not a valid dependency because it never changes. You can safely remove it.',
    
  2346.           suggestions: undefined,
    
  2347.         },
    
  2348.         {
    
  2349.           message:
    
  2350.             'The false literal is not a valid dependency because it never changes. You can safely remove it.',
    
  2351.           suggestions: undefined,
    
  2352.         },
    
  2353.         {
    
  2354.           message:
    
  2355.             'The null literal is not a valid dependency because it never changes. You can safely remove it.',
    
  2356.           suggestions: undefined,
    
  2357.         },
    
  2358.       ],
    
  2359.     },
    
  2360.     {
    
  2361.       code: normalizeIndent`
    
  2362.         function MyComponent() {
    
  2363.           const dependencies = [];
    
  2364.           useEffect(() => {}, dependencies);
    
  2365.         }
    
  2366.       `,
    
  2367.       errors: [
    
  2368.         {
    
  2369.           message:
    
  2370.             'React Hook useEffect was passed a dependency list that is not an ' +
    
  2371.             "array literal. This means we can't statically verify whether you've " +
    
  2372.             'passed the correct dependencies.',
    
  2373.           suggestions: undefined,
    
  2374.         },
    
  2375.       ],
    
  2376.     },
    
  2377.     {
    
  2378.       code: normalizeIndent`
    
  2379.         function MyComponent() {
    
  2380.           const local = {};
    
  2381.           const dependencies = [local];
    
  2382.           useEffect(() => {
    
  2383.             console.log(local);
    
  2384.           }, dependencies);
    
  2385.         }
    
  2386.       `,
    
  2387.       errors: [
    
  2388.         {
    
  2389.           message:
    
  2390.             'React Hook useEffect was passed a dependency list that is not an ' +
    
  2391.             "array literal. This means we can't statically verify whether you've " +
    
  2392.             'passed the correct dependencies.',
    
  2393.           // TODO: should this autofix or bail out?
    
  2394.           suggestions: undefined,
    
  2395.         },
    
  2396.         {
    
  2397.           message:
    
  2398.             "React Hook useEffect has a missing dependency: 'local'. " +
    
  2399.             'Either include it or remove the dependency array.',
    
  2400.           suggestions: [
    
  2401.             {
    
  2402.               desc: 'Update the dependencies array to be: [local]',
    
  2403.               output: normalizeIndent`
    
  2404.                 function MyComponent() {
    
  2405.                   const local = {};
    
  2406.                   const dependencies = [local];
    
  2407.                   useEffect(() => {
    
  2408.                     console.log(local);
    
  2409.                   }, [local]);
    
  2410.                 }
    
  2411.               `,
    
  2412.             },
    
  2413.           ],
    
  2414.         },
    
  2415.       ],
    
  2416.     },
    
  2417.     {
    
  2418.       code: normalizeIndent`
    
  2419.         function MyComponent() {
    
  2420.           const local = {};
    
  2421.           const dependencies = [local];
    
  2422.           useEffect(() => {
    
  2423.             console.log(local);
    
  2424.           }, [...dependencies]);
    
  2425.         }
    
  2426.       `,
    
  2427.       errors: [
    
  2428.         {
    
  2429.           message:
    
  2430.             "React Hook useEffect has a missing dependency: 'local'. " +
    
  2431.             'Either include it or remove the dependency array.',
    
  2432.           suggestions: [
    
  2433.             {
    
  2434.               desc: 'Update the dependencies array to be: [local]',
    
  2435.               output: normalizeIndent`
    
  2436.                 function MyComponent() {
    
  2437.                   const local = {};
    
  2438.                   const dependencies = [local];
    
  2439.                   useEffect(() => {
    
  2440.                     console.log(local);
    
  2441.                   }, [local]);
    
  2442.                 }
    
  2443.               `,
    
  2444.             },
    
  2445.           ],
    
  2446.         },
    
  2447.         {
    
  2448.           message:
    
  2449.             'React Hook useEffect has a spread element in its dependency array. ' +
    
  2450.             "This means we can't statically verify whether you've passed the " +
    
  2451.             'correct dependencies.',
    
  2452.           // TODO: should this autofix or bail out?
    
  2453.           suggestions: undefined,
    
  2454.         },
    
  2455.       ],
    
  2456.     },
    
  2457.     {
    
  2458.       code: normalizeIndent`
    
  2459.         function MyComponent() {
    
  2460.           const local = someFunc();
    
  2461.           useEffect(() => {
    
  2462.             console.log(local);
    
  2463.           }, [local, ...dependencies]);
    
  2464.         }
    
  2465.       `,
    
  2466.       errors: [
    
  2467.         {
    
  2468.           message:
    
  2469.             'React Hook useEffect has a spread element in its dependency array. ' +
    
  2470.             "This means we can't statically verify whether you've passed the " +
    
  2471.             'correct dependencies.',
    
  2472.           suggestions: undefined,
    
  2473.         },
    
  2474.       ],
    
  2475.     },
    
  2476.     {
    
  2477.       code: normalizeIndent`
    
  2478.         function MyComponent() {
    
  2479.           const local = {};
    
  2480.           useEffect(() => {
    
  2481.             console.log(local);
    
  2482.           }, [computeCacheKey(local)]);
    
  2483.         }
    
  2484.       `,
    
  2485.       errors: [
    
  2486.         {
    
  2487.           message:
    
  2488.             "React Hook useEffect has a missing dependency: 'local'. " +
    
  2489.             'Either include it or remove the dependency array.',
    
  2490.           // TODO: I'm not sure this is a good idea.
    
  2491.           // Maybe bail out?
    
  2492.           suggestions: [
    
  2493.             {
    
  2494.               desc: 'Update the dependencies array to be: [local]',
    
  2495.               output: normalizeIndent`
    
  2496.                 function MyComponent() {
    
  2497.                   const local = {};
    
  2498.                   useEffect(() => {
    
  2499.                     console.log(local);
    
  2500.                   }, [local]);
    
  2501.                 }
    
  2502.               `,
    
  2503.             },
    
  2504.           ],
    
  2505.         },
    
  2506.         {
    
  2507.           message:
    
  2508.             'React Hook useEffect has a complex expression in the dependency array. ' +
    
  2509.             'Extract it to a separate variable so it can be statically checked.',
    
  2510.           suggestions: undefined,
    
  2511.         },
    
  2512.       ],
    
  2513.     },
    
  2514.     {
    
  2515.       code: normalizeIndent`
    
  2516.         function MyComponent(props) {
    
  2517.           useEffect(() => {
    
  2518.             console.log(props.items[0]);
    
  2519.           }, [props.items[0]]);
    
  2520.         }
    
  2521.       `,
    
  2522.       errors: [
    
  2523.         {
    
  2524.           message:
    
  2525.             "React Hook useEffect has a missing dependency: 'props.items'. " +
    
  2526.             'Either include it or remove the dependency array.',
    
  2527.           suggestions: [
    
  2528.             {
    
  2529.               desc: 'Update the dependencies array to be: [props.items]',
    
  2530.               output: normalizeIndent`
    
  2531.                 function MyComponent(props) {
    
  2532.                   useEffect(() => {
    
  2533.                     console.log(props.items[0]);
    
  2534.                   }, [props.items]);
    
  2535.                 }
    
  2536.               `,
    
  2537.             },
    
  2538.           ],
    
  2539.         },
    
  2540.         {
    
  2541.           message:
    
  2542.             'React Hook useEffect has a complex expression in the dependency array. ' +
    
  2543.             'Extract it to a separate variable so it can be statically checked.',
    
  2544.           suggestions: undefined,
    
  2545.         },
    
  2546.       ],
    
  2547.     },
    
  2548.     {
    
  2549.       code: normalizeIndent`
    
  2550.         function MyComponent(props) {
    
  2551.           useEffect(() => {
    
  2552.             console.log(props.items[0]);
    
  2553.           }, [props.items, props.items[0]]);
    
  2554.         }
    
  2555.       `,
    
  2556.       errors: [
    
  2557.         {
    
  2558.           message:
    
  2559.             'React Hook useEffect has a complex expression in the dependency array. ' +
    
  2560.             'Extract it to a separate variable so it can be statically checked.',
    
  2561.           // TODO: ideally suggestion would remove the bad expression?
    
  2562.           suggestions: undefined,
    
  2563.         },
    
  2564.       ],
    
  2565.     },
    
  2566.     {
    
  2567.       code: normalizeIndent`
    
  2568.         function MyComponent({ items }) {
    
  2569.           useEffect(() => {
    
  2570.             console.log(items[0]);
    
  2571.           }, [items[0]]);
    
  2572.         }
    
  2573.       `,
    
  2574.       errors: [
    
  2575.         {
    
  2576.           message:
    
  2577.             "React Hook useEffect has a missing dependency: 'items'. " +
    
  2578.             'Either include it or remove the dependency array.',
    
  2579.           suggestions: [
    
  2580.             {
    
  2581.               desc: 'Update the dependencies array to be: [items]',
    
  2582.               output: normalizeIndent`
    
  2583.                 function MyComponent({ items }) {
    
  2584.                   useEffect(() => {
    
  2585.                     console.log(items[0]);
    
  2586.                   }, [items]);
    
  2587.                 }
    
  2588.               `,
    
  2589.             },
    
  2590.           ],
    
  2591.         },
    
  2592.         {
    
  2593.           message:
    
  2594.             'React Hook useEffect has a complex expression in the dependency array. ' +
    
  2595.             'Extract it to a separate variable so it can be statically checked.',
    
  2596.           suggestions: undefined,
    
  2597.         },
    
  2598.       ],
    
  2599.     },
    
  2600.     {
    
  2601.       code: normalizeIndent`
    
  2602.         function MyComponent({ items }) {
    
  2603.           useEffect(() => {
    
  2604.             console.log(items[0]);
    
  2605.           }, [items, items[0]]);
    
  2606.         }
    
  2607.       `,
    
  2608.       errors: [
    
  2609.         {
    
  2610.           message:
    
  2611.             'React Hook useEffect has a complex expression in the dependency array. ' +
    
  2612.             'Extract it to a separate variable so it can be statically checked.',
    
  2613.           // TODO: ideally suggeston would remove the bad expression?
    
  2614.           suggestions: undefined,
    
  2615.         },
    
  2616.       ],
    
  2617.     },
    
  2618.     {
    
  2619.       // It is not valid for useCallback to specify extraneous deps
    
  2620.       // because it doesn't serve as a side effect trigger unlike useEffect.
    
  2621.       // However, we generally allow specifying *broader* deps as escape hatch.
    
  2622.       // So while [props, props.foo] is unnecessary, 'props' wins here as the
    
  2623.       // broader one, and this is why 'props.foo' is reported as unnecessary.
    
  2624.       code: normalizeIndent`
    
  2625.         function MyComponent(props) {
    
  2626.           const local = {};
    
  2627.           useCallback(() => {
    
  2628.             console.log(props.foo);
    
  2629.             console.log(props.bar);
    
  2630.           }, [props, props.foo]);
    
  2631.         }
    
  2632.       `,
    
  2633.       errors: [
    
  2634.         {
    
  2635.           message:
    
  2636.             "React Hook useCallback has an unnecessary dependency: 'props.foo'. " +
    
  2637.             'Either exclude it or remove the dependency array.',
    
  2638.           suggestions: [
    
  2639.             {
    
  2640.               desc: 'Update the dependencies array to be: [props]',
    
  2641.               output: normalizeIndent`
    
  2642.                 function MyComponent(props) {
    
  2643.                   const local = {};
    
  2644.                   useCallback(() => {
    
  2645.                     console.log(props.foo);
    
  2646.                     console.log(props.bar);
    
  2647.                   }, [props]);
    
  2648.                 }
    
  2649.               `,
    
  2650.             },
    
  2651.           ],
    
  2652.         },
    
  2653.       ],
    
  2654.     },
    
  2655.     {
    
  2656.       // Since we don't have 'props' in the list, we'll suggest narrow dependencies.
    
  2657.       code: normalizeIndent`
    
  2658.         function MyComponent(props) {
    
  2659.           const local = {};
    
  2660.           useCallback(() => {
    
  2661.             console.log(props.foo);
    
  2662.             console.log(props.bar);
    
  2663.           }, []);
    
  2664.         }
    
  2665.       `,
    
  2666.       errors: [
    
  2667.         {
    
  2668.           message:
    
  2669.             "React Hook useCallback has missing dependencies: 'props.bar' and 'props.foo'. " +
    
  2670.             'Either include them or remove the dependency array.',
    
  2671.           suggestions: [
    
  2672.             {
    
  2673.               desc: 'Update the dependencies array to be: [props.bar, props.foo]',
    
  2674.               output: normalizeIndent`
    
  2675.                 function MyComponent(props) {
    
  2676.                   const local = {};
    
  2677.                   useCallback(() => {
    
  2678.                     console.log(props.foo);
    
  2679.                     console.log(props.bar);
    
  2680.                   }, [props.bar, props.foo]);
    
  2681.                 }
    
  2682.               `,
    
  2683.             },
    
  2684.           ],
    
  2685.         },
    
  2686.       ],
    
  2687.     },
    
  2688.     {
    
  2689.       // Effects are allowed to over-specify deps. We'll complain about missing
    
  2690.       // 'local', but we won't remove the already-specified 'local.id' from your list.
    
  2691.       code: normalizeIndent`
    
  2692.         function MyComponent() {
    
  2693.           const local = {id: 42};
    
  2694.           useEffect(() => {
    
  2695.             console.log(local);
    
  2696.           }, [local.id]);
    
  2697.         }
    
  2698.       `,
    
  2699.       errors: [
    
  2700.         {
    
  2701.           message:
    
  2702.             "React Hook useEffect has a missing dependency: 'local'. " +
    
  2703.             'Either include it or remove the dependency array.',
    
  2704.           suggestions: [
    
  2705.             {
    
  2706.               desc: 'Update the dependencies array to be: [local, local.id]',
    
  2707.               output: normalizeIndent`
    
  2708.                 function MyComponent() {
    
  2709.                   const local = {id: 42};
    
  2710.                   useEffect(() => {
    
  2711.                     console.log(local);
    
  2712.                   }, [local, local.id]);
    
  2713.                 }
    
  2714.               `,
    
  2715.             },
    
  2716.           ],
    
  2717.         },
    
  2718.       ],
    
  2719.     },
    
  2720.     {
    
  2721.       // Callbacks are not allowed to over-specify deps. So we'll complain about missing
    
  2722.       // 'local' and we will also *remove* 'local.id' from your list.
    
  2723.       code: normalizeIndent`
    
  2724.         function MyComponent() {
    
  2725.           const local = {id: 42};
    
  2726.           const fn = useCallback(() => {
    
  2727.             console.log(local);
    
  2728.           }, [local.id]);
    
  2729.         }
    
  2730.       `,
    
  2731.       errors: [
    
  2732.         {
    
  2733.           message:
    
  2734.             "React Hook useCallback has a missing dependency: 'local'. " +
    
  2735.             'Either include it or remove the dependency array.',
    
  2736.           suggestions: [
    
  2737.             {
    
  2738.               desc: 'Update the dependencies array to be: [local]',
    
  2739.               output: normalizeIndent`
    
  2740.                 function MyComponent() {
    
  2741.                   const local = {id: 42};
    
  2742.                   const fn = useCallback(() => {
    
  2743.                     console.log(local);
    
  2744.                   }, [local]);
    
  2745.                 }
    
  2746.               `,
    
  2747.             },
    
  2748.           ],
    
  2749.         },
    
  2750.       ],
    
  2751.     },
    
  2752.     {
    
  2753.       // Callbacks are not allowed to over-specify deps. So we'll complain about
    
  2754.       // the unnecessary 'local.id'.
    
  2755.       code: normalizeIndent`
    
  2756.         function MyComponent() {
    
  2757.           const local = {id: 42};
    
  2758.           const fn = useCallback(() => {
    
  2759.             console.log(local);
    
  2760.           }, [local.id, local]);
    
  2761.         }
    
  2762.       `,
    
  2763.       errors: [
    
  2764.         {
    
  2765.           message:
    
  2766.             "React Hook useCallback has an unnecessary dependency: 'local.id'. " +
    
  2767.             'Either exclude it or remove the dependency array.',
    
  2768.           suggestions: [
    
  2769.             {
    
  2770.               desc: 'Update the dependencies array to be: [local]',
    
  2771.               output: normalizeIndent`
    
  2772.                 function MyComponent() {
    
  2773.                   const local = {id: 42};
    
  2774.                   const fn = useCallback(() => {
    
  2775.                     console.log(local);
    
  2776.                   }, [local]);
    
  2777.                 }
    
  2778.               `,
    
  2779.             },
    
  2780.           ],
    
  2781.         },
    
  2782.       ],
    
  2783.     },
    
  2784.     {
    
  2785.       code: normalizeIndent`
    
  2786.         function MyComponent(props) {
    
  2787.           const fn = useCallback(() => {
    
  2788.             console.log(props.foo.bar.baz);
    
  2789.           }, []);
    
  2790.         }
    
  2791.       `,
    
  2792.       errors: [
    
  2793.         {
    
  2794.           message:
    
  2795.             "React Hook useCallback has a missing dependency: 'props.foo.bar.baz'. " +
    
  2796.             'Either include it or remove the dependency array.',
    
  2797.           suggestions: [
    
  2798.             {
    
  2799.               desc: 'Update the dependencies array to be: [props.foo.bar.baz]',
    
  2800.               output: normalizeIndent`
    
  2801.                 function MyComponent(props) {
    
  2802.                   const fn = useCallback(() => {
    
  2803.                     console.log(props.foo.bar.baz);
    
  2804.                   }, [props.foo.bar.baz]);
    
  2805.                 }
    
  2806.               `,
    
  2807.             },
    
  2808.           ],
    
  2809.         },
    
  2810.       ],
    
  2811.     },
    
  2812.     {
    
  2813.       code: normalizeIndent`
    
  2814.         function MyComponent(props) {
    
  2815.           let color = {}
    
  2816.           const fn = useCallback(() => {
    
  2817.             console.log(props.foo.bar.baz);
    
  2818.             console.log(color);
    
  2819.           }, [props.foo, props.foo.bar.baz]);
    
  2820.         }
    
  2821.       `,
    
  2822.       errors: [
    
  2823.         {
    
  2824.           message:
    
  2825.             "React Hook useCallback has a missing dependency: 'color'. " +
    
  2826.             'Either include it or remove the dependency array.',
    
  2827.           suggestions: [
    
  2828.             {
    
  2829.               desc: 'Update the dependencies array to be: [color, props.foo.bar.baz]',
    
  2830.               output: normalizeIndent`
    
  2831.                 function MyComponent(props) {
    
  2832.                   let color = {}
    
  2833.                   const fn = useCallback(() => {
    
  2834.                     console.log(props.foo.bar.baz);
    
  2835.                     console.log(color);
    
  2836.                   }, [color, props.foo.bar.baz]);
    
  2837.                 }
    
  2838.               `,
    
  2839.             },
    
  2840.           ],
    
  2841.         },
    
  2842.       ],
    
  2843.     },
    
  2844.     {
    
  2845.       // Callbacks are not allowed to over-specify deps. So one of these is extra.
    
  2846.       // However, it *is* allowed to specify broader deps then strictly necessary.
    
  2847.       // So in this case we ask you to remove 'props.foo.bar.baz' because 'props.foo'
    
  2848.       // already covers it, and having both is unnecessary.
    
  2849.       // TODO: maybe consider suggesting a narrower one by default in these cases.
    
  2850.       code: normalizeIndent`
    
  2851.         function MyComponent(props) {
    
  2852.           const fn = useCallback(() => {
    
  2853.             console.log(props.foo.bar.baz);
    
  2854.           }, [props.foo.bar.baz, props.foo]);
    
  2855.         }
    
  2856.       `,
    
  2857.       errors: [
    
  2858.         {
    
  2859.           message:
    
  2860.             "React Hook useCallback has an unnecessary dependency: 'props.foo.bar.baz'. " +
    
  2861.             'Either exclude it or remove the dependency array.',
    
  2862.           suggestions: [
    
  2863.             {
    
  2864.               desc: 'Update the dependencies array to be: [props.foo]',
    
  2865.               output: normalizeIndent`
    
  2866.                 function MyComponent(props) {
    
  2867.                   const fn = useCallback(() => {
    
  2868.                     console.log(props.foo.bar.baz);
    
  2869.                   }, [props.foo]);
    
  2870.                 }
    
  2871.               `,
    
  2872.             },
    
  2873.           ],
    
  2874.         },
    
  2875.       ],
    
  2876.     },
    
  2877.     {
    
  2878.       code: normalizeIndent`
    
  2879.         function MyComponent(props) {
    
  2880.           const fn = useCallback(() => {
    
  2881.             console.log(props.foo.bar.baz);
    
  2882.             console.log(props.foo.fizz.bizz);
    
  2883.           }, []);
    
  2884.         }
    
  2885.       `,
    
  2886.       errors: [
    
  2887.         {
    
  2888.           message:
    
  2889.             "React Hook useCallback has missing dependencies: 'props.foo.bar.baz' and 'props.foo.fizz.bizz'. " +
    
  2890.             'Either include them or remove the dependency array.',
    
  2891.           suggestions: [
    
  2892.             {
    
  2893.               desc: 'Update the dependencies array to be: [props.foo.bar.baz, props.foo.fizz.bizz]',
    
  2894.               output: normalizeIndent`
    
  2895.                 function MyComponent(props) {
    
  2896.                   const fn = useCallback(() => {
    
  2897.                     console.log(props.foo.bar.baz);
    
  2898.                     console.log(props.foo.fizz.bizz);
    
  2899.                   }, [props.foo.bar.baz, props.foo.fizz.bizz]);
    
  2900.                 }
    
  2901.               `,
    
  2902.             },
    
  2903.           ],
    
  2904.         },
    
  2905.       ],
    
  2906.     },
    
  2907.     {
    
  2908.       // Normally we allow specifying deps too broadly.
    
  2909.       // So we'd be okay if 'props.foo.bar' was there rather than 'props.foo.bar.baz'.
    
  2910.       // However, 'props.foo.bar.baz' is missing. So we know there is a mistake.
    
  2911.       // When we're sure there is a mistake, for callbacks we will rebuild the list
    
  2912.       // from scratch. This will set the user on a better path by default.
    
  2913.       // This is why we end up with just 'props.foo.bar', and not them both.
    
  2914.       code: normalizeIndent`
    
  2915.         function MyComponent(props) {
    
  2916.           const fn = useCallback(() => {
    
  2917.             console.log(props.foo.bar);
    
  2918.           }, [props.foo.bar.baz]);
    
  2919.         }
    
  2920.       `,
    
  2921.       errors: [
    
  2922.         {
    
  2923.           message:
    
  2924.             "React Hook useCallback has a missing dependency: 'props.foo.bar'. " +
    
  2925.             'Either include it or remove the dependency array.',
    
  2926.           suggestions: [
    
  2927.             {
    
  2928.               desc: 'Update the dependencies array to be: [props.foo.bar]',
    
  2929.               output: normalizeIndent`
    
  2930.                 function MyComponent(props) {
    
  2931.                   const fn = useCallback(() => {
    
  2932.                     console.log(props.foo.bar);
    
  2933.                   }, [props.foo.bar]);
    
  2934.                 }
    
  2935.               `,
    
  2936.             },
    
  2937.           ],
    
  2938.         },
    
  2939.       ],
    
  2940.     },
    
  2941.     {
    
  2942.       code: normalizeIndent`
    
  2943.         function MyComponent(props) {
    
  2944.           const fn = useCallback(() => {
    
  2945.             console.log(props);
    
  2946.             console.log(props.hello);
    
  2947.           }, [props.foo.bar.baz]);
    
  2948.         }
    
  2949.       `,
    
  2950.       errors: [
    
  2951.         {
    
  2952.           message:
    
  2953.             "React Hook useCallback has a missing dependency: 'props'. " +
    
  2954.             'Either include it or remove the dependency array.',
    
  2955.           suggestions: [
    
  2956.             {
    
  2957.               desc: 'Update the dependencies array to be: [props]',
    
  2958.               output: normalizeIndent`
    
  2959.                 function MyComponent(props) {
    
  2960.                   const fn = useCallback(() => {
    
  2961.                     console.log(props);
    
  2962.                     console.log(props.hello);
    
  2963.                   }, [props]);
    
  2964.                 }
    
  2965.               `,
    
  2966.             },
    
  2967.           ],
    
  2968.         },
    
  2969.       ],
    
  2970.     },
    
  2971.     {
    
  2972.       code: normalizeIndent`
    
  2973.         function MyComponent() {
    
  2974.           const local = {};
    
  2975.           useEffect(() => {
    
  2976.             console.log(local);
    
  2977.           }, [local, local]);
    
  2978.         }
    
  2979.       `,
    
  2980.       errors: [
    
  2981.         {
    
  2982.           message:
    
  2983.             "React Hook useEffect has a duplicate dependency: 'local'. " +
    
  2984.             'Either omit it or remove the dependency array.',
    
  2985.           suggestions: [
    
  2986.             {
    
  2987.               desc: 'Update the dependencies array to be: [local]',
    
  2988.               output: normalizeIndent`
    
  2989.                 function MyComponent() {
    
  2990.                   const local = {};
    
  2991.                   useEffect(() => {
    
  2992.                     console.log(local);
    
  2993.                   }, [local]);
    
  2994.                 }
    
  2995.               `,
    
  2996.             },
    
  2997.           ],
    
  2998.         },
    
  2999.       ],
    
  3000.     },
    
  3001.     {
    
  3002.       code: normalizeIndent`
    
  3003.         function MyComponent() {
    
  3004.           const local1 = {};
    
  3005.           useCallback(() => {
    
  3006.             const local1 = {};
    
  3007.             console.log(local1);
    
  3008.           }, [local1]);
    
  3009.         }
    
  3010.       `,
    
  3011.       errors: [
    
  3012.         {
    
  3013.           message:
    
  3014.             "React Hook useCallback has an unnecessary dependency: 'local1'. " +
    
  3015.             'Either exclude it or remove the dependency array.',
    
  3016.           suggestions: [
    
  3017.             {
    
  3018.               desc: 'Update the dependencies array to be: []',
    
  3019.               output: normalizeIndent`
    
  3020.                 function MyComponent() {
    
  3021.                   const local1 = {};
    
  3022.                   useCallback(() => {
    
  3023.                     const local1 = {};
    
  3024.                     console.log(local1);
    
  3025.                   }, []);
    
  3026.                 }
    
  3027.               `,
    
  3028.             },
    
  3029.           ],
    
  3030.         },
    
  3031.       ],
    
  3032.     },
    
  3033.     {
    
  3034.       code: normalizeIndent`
    
  3035.         function MyComponent() {
    
  3036.           const local1 = {};
    
  3037.           useCallback(() => {}, [local1]);
    
  3038.         }
    
  3039.       `,
    
  3040.       errors: [
    
  3041.         {
    
  3042.           message:
    
  3043.             "React Hook useCallback has an unnecessary dependency: 'local1'. " +
    
  3044.             'Either exclude it or remove the dependency array.',
    
  3045.           suggestions: [
    
  3046.             {
    
  3047.               desc: 'Update the dependencies array to be: []',
    
  3048.               output: normalizeIndent`
    
  3049.                 function MyComponent() {
    
  3050.                   const local1 = {};
    
  3051.                   useCallback(() => {}, []);
    
  3052.                 }
    
  3053.               `,
    
  3054.             },
    
  3055.           ],
    
  3056.         },
    
  3057.       ],
    
  3058.     },
    
  3059.     {
    
  3060.       code: normalizeIndent`
    
  3061.         function MyComponent(props) {
    
  3062.           useEffect(() => {
    
  3063.             console.log(props.foo);
    
  3064.           }, []);
    
  3065.         }
    
  3066.       `,
    
  3067.       errors: [
    
  3068.         {
    
  3069.           message:
    
  3070.             "React Hook useEffect has a missing dependency: 'props.foo'. " +
    
  3071.             'Either include it or remove the dependency array.',
    
  3072.           suggestions: [
    
  3073.             {
    
  3074.               desc: 'Update the dependencies array to be: [props.foo]',
    
  3075.               output: normalizeIndent`
    
  3076.                 function MyComponent(props) {
    
  3077.                   useEffect(() => {
    
  3078.                     console.log(props.foo);
    
  3079.                   }, [props.foo]);
    
  3080.                 }
    
  3081.               `,
    
  3082.             },
    
  3083.           ],
    
  3084.         },
    
  3085.       ],
    
  3086.     },
    
  3087.     {
    
  3088.       code: normalizeIndent`
    
  3089.         function MyComponent(props) {
    
  3090.           useEffect(() => {
    
  3091.             console.log(props.foo);
    
  3092.             console.log(props.bar);
    
  3093.           }, []);
    
  3094.         }
    
  3095.       `,
    
  3096.       errors: [
    
  3097.         {
    
  3098.           message:
    
  3099.             "React Hook useEffect has missing dependencies: 'props.bar' and 'props.foo'. " +
    
  3100.             'Either include them or remove the dependency array.',
    
  3101.           suggestions: [
    
  3102.             {
    
  3103.               desc: 'Update the dependencies array to be: [props.bar, props.foo]',
    
  3104.               output: normalizeIndent`
    
  3105.                 function MyComponent(props) {
    
  3106.                   useEffect(() => {
    
  3107.                     console.log(props.foo);
    
  3108.                     console.log(props.bar);
    
  3109.                   }, [props.bar, props.foo]);
    
  3110.                 }
    
  3111.               `,
    
  3112.             },
    
  3113.           ],
    
  3114.         },
    
  3115.       ],
    
  3116.     },
    
  3117.     {
    
  3118.       code: normalizeIndent`
    
  3119.         function MyComponent(props) {
    
  3120.           let a, b, c, d, e, f, g;
    
  3121.           useEffect(() => {
    
  3122.             console.log(b, e, d, c, a, g, f);
    
  3123.           }, [c, a, g]);
    
  3124.         }
    
  3125.       `,
    
  3126.       errors: [
    
  3127.         {
    
  3128.           message:
    
  3129.             "React Hook useEffect has missing dependencies: 'b', 'd', 'e', and 'f'. " +
    
  3130.             'Either include them or remove the dependency array.',
    
  3131.           // Don't alphabetize if it wasn't alphabetized in the first place.
    
  3132.           suggestions: [
    
  3133.             {
    
  3134.               desc: 'Update the dependencies array to be: [c, a, g, b, e, d, f]',
    
  3135.               output: normalizeIndent`
    
  3136.                 function MyComponent(props) {
    
  3137.                   let a, b, c, d, e, f, g;
    
  3138.                   useEffect(() => {
    
  3139.                     console.log(b, e, d, c, a, g, f);
    
  3140.                   }, [c, a, g, b, e, d, f]);
    
  3141.                 }
    
  3142.               `,
    
  3143.             },
    
  3144.           ],
    
  3145.         },
    
  3146.       ],
    
  3147.     },
    
  3148.     {
    
  3149.       code: normalizeIndent`
    
  3150.         function MyComponent(props) {
    
  3151.           let a, b, c, d, e, f, g;
    
  3152.           useEffect(() => {
    
  3153.             console.log(b, e, d, c, a, g, f);
    
  3154.           }, [a, c, g]);
    
  3155.         }
    
  3156.       `,
    
  3157.       errors: [
    
  3158.         {
    
  3159.           message:
    
  3160.             "React Hook useEffect has missing dependencies: 'b', 'd', 'e', and 'f'. " +
    
  3161.             'Either include them or remove the dependency array.',
    
  3162.           // Alphabetize if it was alphabetized.
    
  3163.           suggestions: [
    
  3164.             {
    
  3165.               desc: 'Update the dependencies array to be: [a, b, c, d, e, f, g]',
    
  3166.               output: normalizeIndent`
    
  3167.                 function MyComponent(props) {
    
  3168.                   let a, b, c, d, e, f, g;
    
  3169.                   useEffect(() => {
    
  3170.                     console.log(b, e, d, c, a, g, f);
    
  3171.                   }, [a, b, c, d, e, f, g]);
    
  3172.                 }
    
  3173.               `,
    
  3174.             },
    
  3175.           ],
    
  3176.         },
    
  3177.       ],
    
  3178.     },
    
  3179.     {
    
  3180.       code: normalizeIndent`
    
  3181.         function MyComponent(props) {
    
  3182.           let a, b, c, d, e, f, g;
    
  3183.           useEffect(() => {
    
  3184.             console.log(b, e, d, c, a, g, f);
    
  3185.           }, []);
    
  3186.         }
    
  3187.       `,
    
  3188.       errors: [
    
  3189.         {
    
  3190.           message:
    
  3191.             "React Hook useEffect has missing dependencies: 'a', 'b', 'c', 'd', 'e', 'f', and 'g'. " +
    
  3192.             'Either include them or remove the dependency array.',
    
  3193.           // Alphabetize if it was empty.
    
  3194.           suggestions: [
    
  3195.             {
    
  3196.               desc: 'Update the dependencies array to be: [a, b, c, d, e, f, g]',
    
  3197.               output: normalizeIndent`
    
  3198.                 function MyComponent(props) {
    
  3199.                   let a, b, c, d, e, f, g;
    
  3200.                   useEffect(() => {
    
  3201.                     console.log(b, e, d, c, a, g, f);
    
  3202.                   }, [a, b, c, d, e, f, g]);
    
  3203.                 }
    
  3204.               `,
    
  3205.             },
    
  3206.           ],
    
  3207.         },
    
  3208.       ],
    
  3209.     },
    
  3210.     {
    
  3211.       code: normalizeIndent`
    
  3212.         function MyComponent(props) {
    
  3213.           const local = {};
    
  3214.           useEffect(() => {
    
  3215.             console.log(props.foo);
    
  3216.             console.log(props.bar);
    
  3217.             console.log(local);
    
  3218.           }, []);
    
  3219.         }
    
  3220.       `,
    
  3221.       errors: [
    
  3222.         {
    
  3223.           message:
    
  3224.             "React Hook useEffect has missing dependencies: 'local', 'props.bar', and 'props.foo'. " +
    
  3225.             'Either include them or remove the dependency array.',
    
  3226.           suggestions: [
    
  3227.             {
    
  3228.               desc: 'Update the dependencies array to be: [local, props.bar, props.foo]',
    
  3229.               output: normalizeIndent`
    
  3230.                 function MyComponent(props) {
    
  3231.                   const local = {};
    
  3232.                   useEffect(() => {
    
  3233.                     console.log(props.foo);
    
  3234.                     console.log(props.bar);
    
  3235.                     console.log(local);
    
  3236.                   }, [local, props.bar, props.foo]);
    
  3237.                 }
    
  3238.               `,
    
  3239.             },
    
  3240.           ],
    
  3241.         },
    
  3242.       ],
    
  3243.     },
    
  3244.     {
    
  3245.       code: normalizeIndent`
    
  3246.         function MyComponent(props) {
    
  3247.           const local = {};
    
  3248.           useEffect(() => {
    
  3249.             console.log(props.foo);
    
  3250.             console.log(props.bar);
    
  3251.             console.log(local);
    
  3252.           }, [props]);
    
  3253.         }
    
  3254.       `,
    
  3255.       errors: [
    
  3256.         {
    
  3257.           message:
    
  3258.             "React Hook useEffect has a missing dependency: 'local'. " +
    
  3259.             'Either include it or remove the dependency array.',
    
  3260.           suggestions: [
    
  3261.             {
    
  3262.               desc: 'Update the dependencies array to be: [local, props]',
    
  3263.               output: normalizeIndent`
    
  3264.                 function MyComponent(props) {
    
  3265.                   const local = {};
    
  3266.                   useEffect(() => {
    
  3267.                     console.log(props.foo);
    
  3268.                     console.log(props.bar);
    
  3269.                     console.log(local);
    
  3270.                   }, [local, props]);
    
  3271.                 }
    
  3272.               `,
    
  3273.             },
    
  3274.           ],
    
  3275.         },
    
  3276.       ],
    
  3277.     },
    
  3278.     {
    
  3279.       code: normalizeIndent`
    
  3280.         function MyComponent(props) {
    
  3281.           useEffect(() => {
    
  3282.             console.log(props.foo);
    
  3283.           }, []);
    
  3284.           useCallback(() => {
    
  3285.             console.log(props.foo);
    
  3286.           }, []);
    
  3287.           useMemo(() => {
    
  3288.             console.log(props.foo);
    
  3289.           }, []);
    
  3290.           React.useEffect(() => {
    
  3291.             console.log(props.foo);
    
  3292.           }, []);
    
  3293.           React.useCallback(() => {
    
  3294.             console.log(props.foo);
    
  3295.           }, []);
    
  3296.           React.useMemo(() => {
    
  3297.             console.log(props.foo);
    
  3298.           }, []);
    
  3299.           React.notReactiveHook(() => {
    
  3300.             console.log(props.foo);
    
  3301.           }, []);
    
  3302.         }
    
  3303.       `,
    
  3304.       errors: [
    
  3305.         {
    
  3306.           message:
    
  3307.             "React Hook useEffect has a missing dependency: 'props.foo'. " +
    
  3308.             'Either include it or remove the dependency array.',
    
  3309.           suggestions: [
    
  3310.             {
    
  3311.               desc: 'Update the dependencies array to be: [props.foo]',
    
  3312.               output: normalizeIndent`
    
  3313.                 function MyComponent(props) {
    
  3314.                   useEffect(() => {
    
  3315.                     console.log(props.foo);
    
  3316.                   }, [props.foo]);
    
  3317.                   useCallback(() => {
    
  3318.                     console.log(props.foo);
    
  3319.                   }, []);
    
  3320.                   useMemo(() => {
    
  3321.                     console.log(props.foo);
    
  3322.                   }, []);
    
  3323.                   React.useEffect(() => {
    
  3324.                     console.log(props.foo);
    
  3325.                   }, []);
    
  3326.                   React.useCallback(() => {
    
  3327.                     console.log(props.foo);
    
  3328.                   }, []);
    
  3329.                   React.useMemo(() => {
    
  3330.                     console.log(props.foo);
    
  3331.                   }, []);
    
  3332.                   React.notReactiveHook(() => {
    
  3333.                     console.log(props.foo);
    
  3334.                   }, []);
    
  3335.                 }
    
  3336.               `,
    
  3337.             },
    
  3338.           ],
    
  3339.         },
    
  3340.         {
    
  3341.           message:
    
  3342.             "React Hook useCallback has a missing dependency: 'props.foo'. " +
    
  3343.             'Either include it or remove the dependency array.',
    
  3344.           suggestions: [
    
  3345.             {
    
  3346.               desc: 'Update the dependencies array to be: [props.foo]',
    
  3347.               output: normalizeIndent`
    
  3348.                 function MyComponent(props) {
    
  3349.                   useEffect(() => {
    
  3350.                     console.log(props.foo);
    
  3351.                   }, []);
    
  3352.                   useCallback(() => {
    
  3353.                     console.log(props.foo);
    
  3354.                   }, [props.foo]);
    
  3355.                   useMemo(() => {
    
  3356.                     console.log(props.foo);
    
  3357.                   }, []);
    
  3358.                   React.useEffect(() => {
    
  3359.                     console.log(props.foo);
    
  3360.                   }, []);
    
  3361.                   React.useCallback(() => {
    
  3362.                     console.log(props.foo);
    
  3363.                   }, []);
    
  3364.                   React.useMemo(() => {
    
  3365.                     console.log(props.foo);
    
  3366.                   }, []);
    
  3367.                   React.notReactiveHook(() => {
    
  3368.                     console.log(props.foo);
    
  3369.                   }, []);
    
  3370.                 }
    
  3371.               `,
    
  3372.             },
    
  3373.           ],
    
  3374.         },
    
  3375.         {
    
  3376.           message:
    
  3377.             "React Hook useMemo has a missing dependency: 'props.foo'. " +
    
  3378.             'Either include it or remove the dependency array.',
    
  3379.           suggestions: [
    
  3380.             {
    
  3381.               desc: 'Update the dependencies array to be: [props.foo]',
    
  3382.               output: normalizeIndent`
    
  3383.                 function MyComponent(props) {
    
  3384.                   useEffect(() => {
    
  3385.                     console.log(props.foo);
    
  3386.                   }, []);
    
  3387.                   useCallback(() => {
    
  3388.                     console.log(props.foo);
    
  3389.                   }, []);
    
  3390.                   useMemo(() => {
    
  3391.                     console.log(props.foo);
    
  3392.                   }, [props.foo]);
    
  3393.                   React.useEffect(() => {
    
  3394.                     console.log(props.foo);
    
  3395.                   }, []);
    
  3396.                   React.useCallback(() => {
    
  3397.                     console.log(props.foo);
    
  3398.                   }, []);
    
  3399.                   React.useMemo(() => {
    
  3400.                     console.log(props.foo);
    
  3401.                   }, []);
    
  3402.                   React.notReactiveHook(() => {
    
  3403.                     console.log(props.foo);
    
  3404.                   }, []);
    
  3405.                 }
    
  3406.               `,
    
  3407.             },
    
  3408.           ],
    
  3409.         },
    
  3410.         {
    
  3411.           message:
    
  3412.             "React Hook React.useEffect has a missing dependency: 'props.foo'. " +
    
  3413.             'Either include it or remove the dependency array.',
    
  3414.           suggestions: [
    
  3415.             {
    
  3416.               desc: 'Update the dependencies array to be: [props.foo]',
    
  3417.               output: normalizeIndent`
    
  3418.                 function MyComponent(props) {
    
  3419.                   useEffect(() => {
    
  3420.                     console.log(props.foo);
    
  3421.                   }, []);
    
  3422.                   useCallback(() => {
    
  3423.                     console.log(props.foo);
    
  3424.                   }, []);
    
  3425.                   useMemo(() => {
    
  3426.                     console.log(props.foo);
    
  3427.                   }, []);
    
  3428.                   React.useEffect(() => {
    
  3429.                     console.log(props.foo);
    
  3430.                   }, [props.foo]);
    
  3431.                   React.useCallback(() => {
    
  3432.                     console.log(props.foo);
    
  3433.                   }, []);
    
  3434.                   React.useMemo(() => {
    
  3435.                     console.log(props.foo);
    
  3436.                   }, []);
    
  3437.                   React.notReactiveHook(() => {
    
  3438.                     console.log(props.foo);
    
  3439.                   }, []);
    
  3440.                 }
    
  3441.               `,
    
  3442.             },
    
  3443.           ],
    
  3444.         },
    
  3445.         {
    
  3446.           message:
    
  3447.             "React Hook React.useCallback has a missing dependency: 'props.foo'. " +
    
  3448.             'Either include it or remove the dependency array.',
    
  3449.           suggestions: [
    
  3450.             {
    
  3451.               desc: 'Update the dependencies array to be: [props.foo]',
    
  3452.               output: normalizeIndent`
    
  3453.                 function MyComponent(props) {
    
  3454.                   useEffect(() => {
    
  3455.                     console.log(props.foo);
    
  3456.                   }, []);
    
  3457.                   useCallback(() => {
    
  3458.                     console.log(props.foo);
    
  3459.                   }, []);
    
  3460.                   useMemo(() => {
    
  3461.                     console.log(props.foo);
    
  3462.                   }, []);
    
  3463.                   React.useEffect(() => {
    
  3464.                     console.log(props.foo);
    
  3465.                   }, []);
    
  3466.                   React.useCallback(() => {
    
  3467.                     console.log(props.foo);
    
  3468.                   }, [props.foo]);
    
  3469.                   React.useMemo(() => {
    
  3470.                     console.log(props.foo);
    
  3471.                   }, []);
    
  3472.                   React.notReactiveHook(() => {
    
  3473.                     console.log(props.foo);
    
  3474.                   }, []);
    
  3475.                 }
    
  3476.               `,
    
  3477.             },
    
  3478.           ],
    
  3479.         },
    
  3480.         {
    
  3481.           message:
    
  3482.             "React Hook React.useMemo has a missing dependency: 'props.foo'. " +
    
  3483.             'Either include it or remove the dependency array.',
    
  3484.           suggestions: [
    
  3485.             {
    
  3486.               desc: 'Update the dependencies array to be: [props.foo]',
    
  3487.               output: normalizeIndent`
    
  3488.                 function MyComponent(props) {
    
  3489.                   useEffect(() => {
    
  3490.                     console.log(props.foo);
    
  3491.                   }, []);
    
  3492.                   useCallback(() => {
    
  3493.                     console.log(props.foo);
    
  3494.                   }, []);
    
  3495.                   useMemo(() => {
    
  3496.                     console.log(props.foo);
    
  3497.                   }, []);
    
  3498.                   React.useEffect(() => {
    
  3499.                     console.log(props.foo);
    
  3500.                   }, []);
    
  3501.                   React.useCallback(() => {
    
  3502.                     console.log(props.foo);
    
  3503.                   }, []);
    
  3504.                   React.useMemo(() => {
    
  3505.                     console.log(props.foo);
    
  3506.                   }, [props.foo]);
    
  3507.                   React.notReactiveHook(() => {
    
  3508.                     console.log(props.foo);
    
  3509.                   }, []);
    
  3510.                 }
    
  3511.               `,
    
  3512.             },
    
  3513.           ],
    
  3514.         },
    
  3515.       ],
    
  3516.     },
    
  3517.     {
    
  3518.       code: normalizeIndent`
    
  3519.         function MyComponent(props) {
    
  3520.           useCustomEffect(() => {
    
  3521.             console.log(props.foo);
    
  3522.           }, []);
    
  3523.           useEffect(() => {
    
  3524.             console.log(props.foo);
    
  3525.           }, []);
    
  3526.           React.useEffect(() => {
    
  3527.             console.log(props.foo);
    
  3528.           }, []);
    
  3529.           React.useCustomEffect(() => {
    
  3530.             console.log(props.foo);
    
  3531.           }, []);
    
  3532.         }
    
  3533.       `,
    
  3534.       options: [{additionalHooks: 'useCustomEffect'}],
    
  3535.       errors: [
    
  3536.         {
    
  3537.           message:
    
  3538.             "React Hook useCustomEffect has a missing dependency: 'props.foo'. " +
    
  3539.             'Either include it or remove the dependency array.',
    
  3540.           suggestions: [
    
  3541.             {
    
  3542.               desc: 'Update the dependencies array to be: [props.foo]',
    
  3543.               output: normalizeIndent`
    
  3544.                 function MyComponent(props) {
    
  3545.                   useCustomEffect(() => {
    
  3546.                     console.log(props.foo);
    
  3547.                   }, [props.foo]);
    
  3548.                   useEffect(() => {
    
  3549.                     console.log(props.foo);
    
  3550.                   }, []);
    
  3551.                   React.useEffect(() => {
    
  3552.                     console.log(props.foo);
    
  3553.                   }, []);
    
  3554.                   React.useCustomEffect(() => {
    
  3555.                     console.log(props.foo);
    
  3556.                   }, []);
    
  3557.                 }
    
  3558.               `,
    
  3559.             },
    
  3560.           ],
    
  3561.         },
    
  3562.         {
    
  3563.           message:
    
  3564.             "React Hook useEffect has a missing dependency: 'props.foo'. " +
    
  3565.             'Either include it or remove the dependency array.',
    
  3566.           suggestions: [
    
  3567.             {
    
  3568.               desc: 'Update the dependencies array to be: [props.foo]',
    
  3569.               output: normalizeIndent`
    
  3570.                 function MyComponent(props) {
    
  3571.                   useCustomEffect(() => {
    
  3572.                     console.log(props.foo);
    
  3573.                   }, []);
    
  3574.                   useEffect(() => {
    
  3575.                     console.log(props.foo);
    
  3576.                   }, [props.foo]);
    
  3577.                   React.useEffect(() => {
    
  3578.                     console.log(props.foo);
    
  3579.                   }, []);
    
  3580.                   React.useCustomEffect(() => {
    
  3581.                     console.log(props.foo);
    
  3582.                   }, []);
    
  3583.                 }
    
  3584.               `,
    
  3585.             },
    
  3586.           ],
    
  3587.         },
    
  3588.         {
    
  3589.           message:
    
  3590.             "React Hook React.useEffect has a missing dependency: 'props.foo'. " +
    
  3591.             'Either include it or remove the dependency array.',
    
  3592.           suggestions: [
    
  3593.             {
    
  3594.               desc: 'Update the dependencies array to be: [props.foo]',
    
  3595.               output: normalizeIndent`
    
  3596.                 function MyComponent(props) {
    
  3597.                   useCustomEffect(() => {
    
  3598.                     console.log(props.foo);
    
  3599.                   }, []);
    
  3600.                   useEffect(() => {
    
  3601.                     console.log(props.foo);
    
  3602.                   }, []);
    
  3603.                   React.useEffect(() => {
    
  3604.                     console.log(props.foo);
    
  3605.                   }, [props.foo]);
    
  3606.                   React.useCustomEffect(() => {
    
  3607.                     console.log(props.foo);
    
  3608.                   }, []);
    
  3609.                 }
    
  3610.               `,
    
  3611.             },
    
  3612.           ],
    
  3613.         },
    
  3614.       ],
    
  3615.     },
    
  3616.     {
    
  3617.       code: normalizeIndent`
    
  3618.         function MyComponent() {
    
  3619.           const local = {};
    
  3620.           useEffect(() => {
    
  3621.             console.log(local);
    
  3622.           }, [a ? local : b]);
    
  3623.         }
    
  3624.       `,
    
  3625.       errors: [
    
  3626.         {
    
  3627.           message:
    
  3628.             "React Hook useEffect has a missing dependency: 'local'. " +
    
  3629.             'Either include it or remove the dependency array.',
    
  3630.           // TODO: should we bail out instead?
    
  3631.           suggestions: [
    
  3632.             {
    
  3633.               desc: 'Update the dependencies array to be: [local]',
    
  3634.               output: normalizeIndent`
    
  3635.                 function MyComponent() {
    
  3636.                   const local = {};
    
  3637.                   useEffect(() => {
    
  3638.                     console.log(local);
    
  3639.                   }, [local]);
    
  3640.                 }
    
  3641.               `,
    
  3642.             },
    
  3643.           ],
    
  3644.         },
    
  3645.         {
    
  3646.           message:
    
  3647.             'React Hook useEffect has a complex expression in the dependency array. ' +
    
  3648.             'Extract it to a separate variable so it can be statically checked.',
    
  3649.           suggestions: undefined,
    
  3650.         },
    
  3651.       ],
    
  3652.     },
    
  3653.     {
    
  3654.       code: normalizeIndent`
    
  3655.         function MyComponent() {
    
  3656.           const local = {};
    
  3657.           useEffect(() => {
    
  3658.             console.log(local);
    
  3659.           }, [a && local]);
    
  3660.         }
    
  3661.       `,
    
  3662.       errors: [
    
  3663.         {
    
  3664.           message:
    
  3665.             "React Hook useEffect has a missing dependency: 'local'. " +
    
  3666.             'Either include it or remove the dependency array.',
    
  3667.           // TODO: should we bail out instead?
    
  3668.           suggestions: [
    
  3669.             {
    
  3670.               desc: 'Update the dependencies array to be: [local]',
    
  3671.               output: normalizeIndent`
    
  3672.                 function MyComponent() {
    
  3673.                   const local = {};
    
  3674.                   useEffect(() => {
    
  3675.                     console.log(local);
    
  3676.                   }, [local]);
    
  3677.                 }
    
  3678.               `,
    
  3679.             },
    
  3680.           ],
    
  3681.         },
    
  3682.         {
    
  3683.           message:
    
  3684.             'React Hook useEffect has a complex expression in the dependency array. ' +
    
  3685.             'Extract it to a separate variable so it can be statically checked.',
    
  3686.           suggestions: undefined,
    
  3687.         },
    
  3688.       ],
    
  3689.     },
    
  3690.     {
    
  3691.       code: normalizeIndent`
    
  3692.         function MyComponent(props) {
    
  3693.           useEffect(() => {}, [props?.attribute.method()]);
    
  3694.         }
    
  3695.       `,
    
  3696.       errors: [
    
  3697.         {
    
  3698.           message:
    
  3699.             'React Hook useEffect has a complex expression in the dependency array. ' +
    
  3700.             'Extract it to a separate variable so it can be statically checked.',
    
  3701.           suggestions: undefined,
    
  3702.         },
    
  3703.       ],
    
  3704.     },
    
  3705.     {
    
  3706.       code: normalizeIndent`
    
  3707.         function MyComponent(props) {
    
  3708.           useEffect(() => {}, [props.method()]);
    
  3709.         }
    
  3710.       `,
    
  3711.       errors: [
    
  3712.         {
    
  3713.           message:
    
  3714.             'React Hook useEffect has a complex expression in the dependency array. ' +
    
  3715.             'Extract it to a separate variable so it can be statically checked.',
    
  3716.           suggestions: undefined,
    
  3717.         },
    
  3718.       ],
    
  3719.     },
    
  3720.     {
    
  3721.       code: normalizeIndent`
    
  3722.         function MyComponent() {
    
  3723.           const ref = useRef();
    
  3724.           const [state, setState] = useState();
    
  3725.           useEffect(() => {
    
  3726.             ref.current = {};
    
  3727.             setState(state + 1);
    
  3728.           }, []);
    
  3729.         }
    
  3730.       `,
    
  3731.       errors: [
    
  3732.         {
    
  3733.           message:
    
  3734.             "React Hook useEffect has a missing dependency: 'state'. " +
    
  3735.             'Either include it or remove the dependency array. ' +
    
  3736.             `You can also do a functional update 'setState(s => ...)' ` +
    
  3737.             `if you only need 'state' in the 'setState' call.`,
    
  3738.           suggestions: [
    
  3739.             {
    
  3740.               desc: 'Update the dependencies array to be: [state]',
    
  3741.               output: normalizeIndent`
    
  3742.                 function MyComponent() {
    
  3743.                   const ref = useRef();
    
  3744.                   const [state, setState] = useState();
    
  3745.                   useEffect(() => {
    
  3746.                     ref.current = {};
    
  3747.                     setState(state + 1);
    
  3748.                   }, [state]);
    
  3749.                 }
    
  3750.               `,
    
  3751.             },
    
  3752.           ],
    
  3753.         },
    
  3754.       ],
    
  3755.     },
    
  3756.     {
    
  3757.       code: normalizeIndent`
    
  3758.         function MyComponent() {
    
  3759.           const ref = useRef();
    
  3760.           const [state, setState] = useState();
    
  3761.           useEffect(() => {
    
  3762.             ref.current = {};
    
  3763.             setState(state + 1);
    
  3764.           }, [ref]);
    
  3765.         }
    
  3766.       `,
    
  3767.       errors: [
    
  3768.         {
    
  3769.           message:
    
  3770.             "React Hook useEffect has a missing dependency: 'state'. " +
    
  3771.             'Either include it or remove the dependency array. ' +
    
  3772.             `You can also do a functional update 'setState(s => ...)' ` +
    
  3773.             `if you only need 'state' in the 'setState' call.`,
    
  3774.           // We don't ask to remove static deps but don't add them either.
    
  3775.           // Don't suggest removing "ref" (it's fine either way)
    
  3776.           // but *do* add "state". *Don't* add "setState" ourselves.
    
  3777.           suggestions: [
    
  3778.             {
    
  3779.               desc: 'Update the dependencies array to be: [ref, state]',
    
  3780.               output: normalizeIndent`
    
  3781.                 function MyComponent() {
    
  3782.                   const ref = useRef();
    
  3783.                   const [state, setState] = useState();
    
  3784.                   useEffect(() => {
    
  3785.                     ref.current = {};
    
  3786.                     setState(state + 1);
    
  3787.                   }, [ref, state]);
    
  3788.                 }
    
  3789.               `,
    
  3790.             },
    
  3791.           ],
    
  3792.         },
    
  3793.       ],
    
  3794.     },
    
  3795.     {
    
  3796.       code: normalizeIndent`
    
  3797.         function MyComponent(props) {
    
  3798.           const ref1 = useRef();
    
  3799.           const ref2 = useRef();
    
  3800.           useEffect(() => {
    
  3801.             ref1.current.focus();
    
  3802.             console.log(ref2.current.textContent);
    
  3803.             alert(props.someOtherRefs.current.innerHTML);
    
  3804.             fetch(props.color);
    
  3805.           }, []);
    
  3806.         }
    
  3807.       `,
    
  3808.       errors: [
    
  3809.         {
    
  3810.           message:
    
  3811.             "React Hook useEffect has missing dependencies: 'props.color' and 'props.someOtherRefs'. " +
    
  3812.             'Either include them or remove the dependency array.',
    
  3813.           suggestions: [
    
  3814.             {
    
  3815.               desc: 'Update the dependencies array to be: [props.color, props.someOtherRefs]',
    
  3816.               output: normalizeIndent`
    
  3817.                 function MyComponent(props) {
    
  3818.                   const ref1 = useRef();
    
  3819.                   const ref2 = useRef();
    
  3820.                   useEffect(() => {
    
  3821.                     ref1.current.focus();
    
  3822.                     console.log(ref2.current.textContent);
    
  3823.                     alert(props.someOtherRefs.current.innerHTML);
    
  3824.                     fetch(props.color);
    
  3825.                   }, [props.color, props.someOtherRefs]);
    
  3826.                 }
    
  3827.               `,
    
  3828.             },
    
  3829.           ],
    
  3830.         },
    
  3831.       ],
    
  3832.     },
    
  3833.     {
    
  3834.       code: normalizeIndent`
    
  3835.         function MyComponent(props) {
    
  3836.           const ref1 = useRef();
    
  3837.           const ref2 = useRef();
    
  3838.           useEffect(() => {
    
  3839.             ref1.current.focus();
    
  3840.             console.log(ref2.current.textContent);
    
  3841.             alert(props.someOtherRefs.current.innerHTML);
    
  3842.             fetch(props.color);
    
  3843.           }, [ref1.current, ref2.current, props.someOtherRefs, props.color]);
    
  3844.         }
    
  3845.       `,
    
  3846.       errors: [
    
  3847.         {
    
  3848.           message:
    
  3849.             "React Hook useEffect has unnecessary dependencies: 'ref1.current' and 'ref2.current'. " +
    
  3850.             'Either exclude them or remove the dependency array. ' +
    
  3851.             "Mutable values like 'ref1.current' aren't valid dependencies " +
    
  3852.             "because mutating them doesn't re-render the component.",
    
  3853.           suggestions: [
    
  3854.             {
    
  3855.               desc: 'Update the dependencies array to be: [props.someOtherRefs, props.color]',
    
  3856.               output: normalizeIndent`
    
  3857.                 function MyComponent(props) {
    
  3858.                   const ref1 = useRef();
    
  3859.                   const ref2 = useRef();
    
  3860.                   useEffect(() => {
    
  3861.                     ref1.current.focus();
    
  3862.                     console.log(ref2.current.textContent);
    
  3863.                     alert(props.someOtherRefs.current.innerHTML);
    
  3864.                     fetch(props.color);
    
  3865.                   }, [props.someOtherRefs, props.color]);
    
  3866.                 }
    
  3867.               `,
    
  3868.             },
    
  3869.           ],
    
  3870.         },
    
  3871.       ],
    
  3872.     },
    
  3873.     {
    
  3874.       code: normalizeIndent`
    
  3875.         function MyComponent(props) {
    
  3876.           const ref1 = useRef();
    
  3877.           const ref2 = useRef();
    
  3878.           useEffect(() => {
    
  3879.             ref1?.current?.focus();
    
  3880.             console.log(ref2?.current?.textContent);
    
  3881.             alert(props.someOtherRefs.current.innerHTML);
    
  3882.             fetch(props.color);
    
  3883.           }, [ref1?.current, ref2?.current, props.someOtherRefs, props.color]);
    
  3884.         }
    
  3885.       `,
    
  3886.       errors: [
    
  3887.         {
    
  3888.           message:
    
  3889.             "React Hook useEffect has unnecessary dependencies: 'ref1.current' and 'ref2.current'. " +
    
  3890.             'Either exclude them or remove the dependency array. ' +
    
  3891.             "Mutable values like 'ref1.current' aren't valid dependencies " +
    
  3892.             "because mutating them doesn't re-render the component.",
    
  3893.           suggestions: [
    
  3894.             {
    
  3895.               desc: 'Update the dependencies array to be: [props.someOtherRefs, props.color]',
    
  3896.               output: normalizeIndent`
    
  3897.                 function MyComponent(props) {
    
  3898.                   const ref1 = useRef();
    
  3899.                   const ref2 = useRef();
    
  3900.                   useEffect(() => {
    
  3901.                     ref1?.current?.focus();
    
  3902.                     console.log(ref2?.current?.textContent);
    
  3903.                     alert(props.someOtherRefs.current.innerHTML);
    
  3904.                     fetch(props.color);
    
  3905.                   }, [props.someOtherRefs, props.color]);
    
  3906.                 }
    
  3907.               `,
    
  3908.             },
    
  3909.           ],
    
  3910.         },
    
  3911.       ],
    
  3912.     },
    
  3913.     {
    
  3914.       code: normalizeIndent`
    
  3915.         function MyComponent() {
    
  3916.           const ref = useRef();
    
  3917.           useEffect(() => {
    
  3918.             console.log(ref.current);
    
  3919.           }, [ref.current]);
    
  3920.         }
    
  3921.       `,
    
  3922.       errors: [
    
  3923.         {
    
  3924.           message:
    
  3925.             "React Hook useEffect has an unnecessary dependency: 'ref.current'. " +
    
  3926.             'Either exclude it or remove the dependency array. ' +
    
  3927.             "Mutable values like 'ref.current' aren't valid dependencies " +
    
  3928.             "because mutating them doesn't re-render the component.",
    
  3929.           suggestions: [
    
  3930.             {
    
  3931.               desc: 'Update the dependencies array to be: []',
    
  3932.               output: normalizeIndent`
    
  3933.                 function MyComponent() {
    
  3934.                   const ref = useRef();
    
  3935.                   useEffect(() => {
    
  3936.                     console.log(ref.current);
    
  3937.                   }, []);
    
  3938.                 }
    
  3939.               `,
    
  3940.             },
    
  3941.           ],
    
  3942.         },
    
  3943.       ],
    
  3944.     },
    
  3945.     {
    
  3946.       code: normalizeIndent`
    
  3947.         function MyComponent({ activeTab }) {
    
  3948.           const ref1 = useRef();
    
  3949.           const ref2 = useRef();
    
  3950.           useEffect(() => {
    
  3951.             ref1.current.scrollTop = 0;
    
  3952.             ref2.current.scrollTop = 0;
    
  3953.           }, [ref1.current, ref2.current, activeTab]);
    
  3954.         }
    
  3955.       `,
    
  3956.       errors: [
    
  3957.         {
    
  3958.           message:
    
  3959.             "React Hook useEffect has unnecessary dependencies: 'ref1.current' and 'ref2.current'. " +
    
  3960.             'Either exclude them or remove the dependency array. ' +
    
  3961.             "Mutable values like 'ref1.current' aren't valid dependencies " +
    
  3962.             "because mutating them doesn't re-render the component.",
    
  3963.           suggestions: [
    
  3964.             {
    
  3965.               desc: 'Update the dependencies array to be: [activeTab]',
    
  3966.               output: normalizeIndent`
    
  3967.                 function MyComponent({ activeTab }) {
    
  3968.                   const ref1 = useRef();
    
  3969.                   const ref2 = useRef();
    
  3970.                   useEffect(() => {
    
  3971.                     ref1.current.scrollTop = 0;
    
  3972.                     ref2.current.scrollTop = 0;
    
  3973.                   }, [activeTab]);
    
  3974.                 }
    
  3975.               `,
    
  3976.             },
    
  3977.           ],
    
  3978.         },
    
  3979.       ],
    
  3980.     },
    
  3981.     {
    
  3982.       code: normalizeIndent`
    
  3983.         function MyComponent({ activeTab, initY }) {
    
  3984.           const ref1 = useRef();
    
  3985.           const ref2 = useRef();
    
  3986.           const fn = useCallback(() => {
    
  3987.             ref1.current.scrollTop = initY;
    
  3988.             ref2.current.scrollTop = initY;
    
  3989.           }, [ref1.current, ref2.current, activeTab, initY]);
    
  3990.         }
    
  3991.       `,
    
  3992.       errors: [
    
  3993.         {
    
  3994.           message:
    
  3995.             "React Hook useCallback has unnecessary dependencies: 'activeTab', 'ref1.current', and 'ref2.current'. " +
    
  3996.             'Either exclude them or remove the dependency array. ' +
    
  3997.             "Mutable values like 'ref1.current' aren't valid dependencies " +
    
  3998.             "because mutating them doesn't re-render the component.",
    
  3999.           suggestions: [
    
  4000.             {
    
  4001.               desc: 'Update the dependencies array to be: [initY]',
    
  4002.               output: normalizeIndent`
    
  4003.                 function MyComponent({ activeTab, initY }) {
    
  4004.                   const ref1 = useRef();
    
  4005.                   const ref2 = useRef();
    
  4006.                   const fn = useCallback(() => {
    
  4007.                     ref1.current.scrollTop = initY;
    
  4008.                     ref2.current.scrollTop = initY;
    
  4009.                   }, [initY]);
    
  4010.                 }
    
  4011.               `,
    
  4012.             },
    
  4013.           ],
    
  4014.         },
    
  4015.       ],
    
  4016.     },
    
  4017.     {
    
  4018.       code: normalizeIndent`
    
  4019.         function MyComponent() {
    
  4020.           const ref = useRef();
    
  4021.           useEffect(() => {
    
  4022.             console.log(ref.current);
    
  4023.           }, [ref.current, ref]);
    
  4024.         }
    
  4025.       `,
    
  4026.       errors: [
    
  4027.         {
    
  4028.           message:
    
  4029.             "React Hook useEffect has an unnecessary dependency: 'ref.current'. " +
    
  4030.             'Either exclude it or remove the dependency array. ' +
    
  4031.             "Mutable values like 'ref.current' aren't valid dependencies " +
    
  4032.             "because mutating them doesn't re-render the component.",
    
  4033.           suggestions: [
    
  4034.             {
    
  4035.               desc: 'Update the dependencies array to be: [ref]',
    
  4036.               output: normalizeIndent`
    
  4037.                 function MyComponent() {
    
  4038.                   const ref = useRef();
    
  4039.                   useEffect(() => {
    
  4040.                     console.log(ref.current);
    
  4041.                   }, [ref]);
    
  4042.                 }
    
  4043.               `,
    
  4044.             },
    
  4045.           ],
    
  4046.         },
    
  4047.       ],
    
  4048.     },
    
  4049.     {
    
  4050.       code: normalizeIndent`
    
  4051.         const MyComponent = forwardRef((props, ref) => {
    
  4052.           useImperativeHandle(ref, () => ({
    
  4053.             focus() {
    
  4054.               alert(props.hello);
    
  4055.             }
    
  4056.           }), [])
    
  4057.         });
    
  4058.       `,
    
  4059.       errors: [
    
  4060.         {
    
  4061.           message:
    
  4062.             "React Hook useImperativeHandle has a missing dependency: 'props.hello'. " +
    
  4063.             'Either include it or remove the dependency array.',
    
  4064.           suggestions: [
    
  4065.             {
    
  4066.               desc: 'Update the dependencies array to be: [props.hello]',
    
  4067.               output: normalizeIndent`
    
  4068.                 const MyComponent = forwardRef((props, ref) => {
    
  4069.                   useImperativeHandle(ref, () => ({
    
  4070.                     focus() {
    
  4071.                       alert(props.hello);
    
  4072.                     }
    
  4073.                   }), [props.hello])
    
  4074.                 });
    
  4075.               `,
    
  4076.             },
    
  4077.           ],
    
  4078.         },
    
  4079.       ],
    
  4080.     },
    
  4081.     {
    
  4082.       code: normalizeIndent`
    
  4083.         function MyComponent(props) {
    
  4084.           useEffect(() => {
    
  4085.             if (props.onChange) {
    
  4086.               props.onChange();
    
  4087.             }
    
  4088.           }, []);
    
  4089.         }
    
  4090.       `,
    
  4091.       errors: [
    
  4092.         {
    
  4093.           message:
    
  4094.             "React Hook useEffect has a missing dependency: 'props'. " +
    
  4095.             'Either include it or remove the dependency array. ' +
    
  4096.             `However, 'props' will change when *any* prop changes, so the ` +
    
  4097.             `preferred fix is to destructure the 'props' object outside ` +
    
  4098.             `of the useEffect call and refer to those specific ` +
    
  4099.             `props inside useEffect.`,
    
  4100.           suggestions: [
    
  4101.             {
    
  4102.               desc: 'Update the dependencies array to be: [props]',
    
  4103.               output: normalizeIndent`
    
  4104.                 function MyComponent(props) {
    
  4105.                   useEffect(() => {
    
  4106.                     if (props.onChange) {
    
  4107.                       props.onChange();
    
  4108.                     }
    
  4109.                   }, [props]);
    
  4110.                 }
    
  4111.               `,
    
  4112.             },
    
  4113.           ],
    
  4114.         },
    
  4115.       ],
    
  4116.     },
    
  4117.     {
    
  4118.       code: normalizeIndent`
    
  4119.         function MyComponent(props) {
    
  4120.           useEffect(() => {
    
  4121.             if (props?.onChange) {
    
  4122.               props?.onChange();
    
  4123.             }
    
  4124.           }, []);
    
  4125.         }
    
  4126.       `,
    
  4127.       errors: [
    
  4128.         {
    
  4129.           message:
    
  4130.             "React Hook useEffect has a missing dependency: 'props'. " +
    
  4131.             'Either include it or remove the dependency array. ' +
    
  4132.             `However, 'props' will change when *any* prop changes, so the ` +
    
  4133.             `preferred fix is to destructure the 'props' object outside ` +
    
  4134.             `of the useEffect call and refer to those specific ` +
    
  4135.             `props inside useEffect.`,
    
  4136.           suggestions: [
    
  4137.             {
    
  4138.               desc: 'Update the dependencies array to be: [props]',
    
  4139.               output: normalizeIndent`
    
  4140.                 function MyComponent(props) {
    
  4141.                   useEffect(() => {
    
  4142.                     if (props?.onChange) {
    
  4143.                       props?.onChange();
    
  4144.                     }
    
  4145.                   }, [props]);
    
  4146.                 }
    
  4147.               `,
    
  4148.             },
    
  4149.           ],
    
  4150.         },
    
  4151.       ],
    
  4152.     },
    
  4153.     {
    
  4154.       code: normalizeIndent`
    
  4155.         function MyComponent(props) {
    
  4156.           useEffect(() => {
    
  4157.             function play() {
    
  4158.               props.onPlay();
    
  4159.             }
    
  4160.             function pause() {
    
  4161.               props.onPause();
    
  4162.             }
    
  4163.           }, []);
    
  4164.         }
    
  4165.       `,
    
  4166.       errors: [
    
  4167.         {
    
  4168.           message:
    
  4169.             "React Hook useEffect has a missing dependency: 'props'. " +
    
  4170.             'Either include it or remove the dependency array. ' +
    
  4171.             `However, 'props' will change when *any* prop changes, so the ` +
    
  4172.             `preferred fix is to destructure the 'props' object outside ` +
    
  4173.             `of the useEffect call and refer to those specific ` +
    
  4174.             `props inside useEffect.`,
    
  4175.           suggestions: [
    
  4176.             {
    
  4177.               desc: 'Update the dependencies array to be: [props]',
    
  4178.               output: normalizeIndent`
    
  4179.                 function MyComponent(props) {
    
  4180.                   useEffect(() => {
    
  4181.                     function play() {
    
  4182.                       props.onPlay();
    
  4183.                     }
    
  4184.                     function pause() {
    
  4185.                       props.onPause();
    
  4186.                     }
    
  4187.                   }, [props]);
    
  4188.                 }
    
  4189.               `,
    
  4190.             },
    
  4191.           ],
    
  4192.         },
    
  4193.       ],
    
  4194.     },
    
  4195.     {
    
  4196.       code: normalizeIndent`
    
  4197.         function MyComponent(props) {
    
  4198.           useEffect(() => {
    
  4199.             if (props.foo.onChange) {
    
  4200.               props.foo.onChange();
    
  4201.             }
    
  4202.           }, []);
    
  4203.         }
    
  4204.       `,
    
  4205.       errors: [
    
  4206.         {
    
  4207.           message:
    
  4208.             "React Hook useEffect has a missing dependency: 'props.foo'. " +
    
  4209.             'Either include it or remove the dependency array.',
    
  4210.           suggestions: [
    
  4211.             {
    
  4212.               desc: 'Update the dependencies array to be: [props.foo]',
    
  4213.               output: normalizeIndent`
    
  4214.                 function MyComponent(props) {
    
  4215.                   useEffect(() => {
    
  4216.                     if (props.foo.onChange) {
    
  4217.                       props.foo.onChange();
    
  4218.                     }
    
  4219.                   }, [props.foo]);
    
  4220.                 }
    
  4221.               `,
    
  4222.             },
    
  4223.           ],
    
  4224.         },
    
  4225.       ],
    
  4226.     },
    
  4227.     {
    
  4228.       code: normalizeIndent`
    
  4229.         function MyComponent(props) {
    
  4230.           useEffect(() => {
    
  4231.             props.onChange();
    
  4232.             if (props.foo.onChange) {
    
  4233.               props.foo.onChange();
    
  4234.             }
    
  4235.           }, []);
    
  4236.         }
    
  4237.       `,
    
  4238.       errors: [
    
  4239.         {
    
  4240.           message:
    
  4241.             "React Hook useEffect has a missing dependency: 'props'. " +
    
  4242.             'Either include it or remove the dependency array. ' +
    
  4243.             `However, 'props' will change when *any* prop changes, so the ` +
    
  4244.             `preferred fix is to destructure the 'props' object outside ` +
    
  4245.             `of the useEffect call and refer to those specific ` +
    
  4246.             `props inside useEffect.`,
    
  4247.           suggestions: [
    
  4248.             {
    
  4249.               desc: 'Update the dependencies array to be: [props]',
    
  4250.               output: normalizeIndent`
    
  4251.                 function MyComponent(props) {
    
  4252.                   useEffect(() => {
    
  4253.                     props.onChange();
    
  4254.                     if (props.foo.onChange) {
    
  4255.                       props.foo.onChange();
    
  4256.                     }
    
  4257.                   }, [props]);
    
  4258.                 }
    
  4259.               `,
    
  4260.             },
    
  4261.           ],
    
  4262.         },
    
  4263.       ],
    
  4264.     },
    
  4265.     {
    
  4266.       code: normalizeIndent`
    
  4267.         function MyComponent(props) {
    
  4268.           const [skillsCount] = useState();
    
  4269.           useEffect(() => {
    
  4270.             if (skillsCount === 0 && !props.isEditMode) {
    
  4271.               props.toggleEditMode();
    
  4272.             }
    
  4273.           }, [skillsCount, props.isEditMode, props.toggleEditMode]);
    
  4274.         }
    
  4275.       `,
    
  4276.       errors: [
    
  4277.         {
    
  4278.           message:
    
  4279.             "React Hook useEffect has a missing dependency: 'props'. " +
    
  4280.             'Either include it or remove the dependency array. ' +
    
  4281.             `However, 'props' will change when *any* prop changes, so the ` +
    
  4282.             `preferred fix is to destructure the 'props' object outside ` +
    
  4283.             `of the useEffect call and refer to those specific ` +
    
  4284.             `props inside useEffect.`,
    
  4285.           suggestions: [
    
  4286.             {
    
  4287.               desc: 'Update the dependencies array to be: [skillsCount, props.isEditMode, props.toggleEditMode, props]',
    
  4288.               output: normalizeIndent`
    
  4289.                 function MyComponent(props) {
    
  4290.                   const [skillsCount] = useState();
    
  4291.                   useEffect(() => {
    
  4292.                     if (skillsCount === 0 && !props.isEditMode) {
    
  4293.                       props.toggleEditMode();
    
  4294.                     }
    
  4295.                   }, [skillsCount, props.isEditMode, props.toggleEditMode, props]);
    
  4296.                 }
    
  4297.               `,
    
  4298.             },
    
  4299.           ],
    
  4300.         },
    
  4301.       ],
    
  4302.     },
    
  4303.     {
    
  4304.       code: normalizeIndent`
    
  4305.         function MyComponent(props) {
    
  4306.           const [skillsCount] = useState();
    
  4307.           useEffect(() => {
    
  4308.             if (skillsCount === 0 && !props.isEditMode) {
    
  4309.               props.toggleEditMode();
    
  4310.             }
    
  4311.           }, []);
    
  4312.         }
    
  4313.       `,
    
  4314.       errors: [
    
  4315.         {
    
  4316.           message:
    
  4317.             "React Hook useEffect has missing dependencies: 'props' and 'skillsCount'. " +
    
  4318.             'Either include them or remove the dependency array. ' +
    
  4319.             `However, 'props' will change when *any* prop changes, so the ` +
    
  4320.             `preferred fix is to destructure the 'props' object outside ` +
    
  4321.             `of the useEffect call and refer to those specific ` +
    
  4322.             `props inside useEffect.`,
    
  4323.           suggestions: [
    
  4324.             {
    
  4325.               desc: 'Update the dependencies array to be: [props, skillsCount]',
    
  4326.               output: normalizeIndent`
    
  4327.                 function MyComponent(props) {
    
  4328.                   const [skillsCount] = useState();
    
  4329.                   useEffect(() => {
    
  4330.                     if (skillsCount === 0 && !props.isEditMode) {
    
  4331.                       props.toggleEditMode();
    
  4332.                     }
    
  4333.                   }, [props, skillsCount]);
    
  4334.                 }
    
  4335.               `,
    
  4336.             },
    
  4337.           ],
    
  4338.         },
    
  4339.       ],
    
  4340.     },
    
  4341.     {
    
  4342.       code: normalizeIndent`
    
  4343.         function MyComponent(props) {
    
  4344.           useEffect(() => {
    
  4345.             externalCall(props);
    
  4346.             props.onChange();
    
  4347.           }, []);
    
  4348.         }
    
  4349.       `,
    
  4350.       // Don't suggest to destructure props here since you can't.
    
  4351.       errors: [
    
  4352.         {
    
  4353.           message:
    
  4354.             "React Hook useEffect has a missing dependency: 'props'. " +
    
  4355.             'Either include it or remove the dependency array.',
    
  4356.           suggestions: [
    
  4357.             {
    
  4358.               desc: 'Update the dependencies array to be: [props]',
    
  4359.               output: normalizeIndent`
    
  4360.                 function MyComponent(props) {
    
  4361.                   useEffect(() => {
    
  4362.                     externalCall(props);
    
  4363.                     props.onChange();
    
  4364.                   }, [props]);
    
  4365.                 }
    
  4366.               `,
    
  4367.             },
    
  4368.           ],
    
  4369.         },
    
  4370.       ],
    
  4371.     },
    
  4372.     {
    
  4373.       code: normalizeIndent`
    
  4374.         function MyComponent(props) {
    
  4375.           useEffect(() => {
    
  4376.             props.onChange();
    
  4377.             externalCall(props);
    
  4378.           }, []);
    
  4379.         }
    
  4380.       `,
    
  4381.       // Don't suggest to destructure props here since you can't.
    
  4382.       errors: [
    
  4383.         {
    
  4384.           message:
    
  4385.             "React Hook useEffect has a missing dependency: 'props'. " +
    
  4386.             'Either include it or remove the dependency array.',
    
  4387.           suggestions: [
    
  4388.             {
    
  4389.               desc: 'Update the dependencies array to be: [props]',
    
  4390.               output: normalizeIndent`
    
  4391.                 function MyComponent(props) {
    
  4392.                   useEffect(() => {
    
  4393.                     props.onChange();
    
  4394.                     externalCall(props);
    
  4395.                   }, [props]);
    
  4396.                 }
    
  4397.               `,
    
  4398.             },
    
  4399.           ],
    
  4400.         },
    
  4401.       ],
    
  4402.     },
    
  4403.     {
    
  4404.       code: normalizeIndent`
    
  4405.         function MyComponent(props) {
    
  4406.           let value;
    
  4407.           let value2;
    
  4408.           let value3;
    
  4409.           let value4;
    
  4410.           let asyncValue;
    
  4411.           useEffect(() => {
    
  4412.             if (value4) {
    
  4413.               value = {};
    
  4414.             }
    
  4415.             value2 = 100;
    
  4416.             value = 43;
    
  4417.             value4 = true;
    
  4418.             console.log(value2);
    
  4419.             console.log(value3);
    
  4420.             setTimeout(() => {
    
  4421.               asyncValue = 100;
    
  4422.             });
    
  4423.           }, []);
    
  4424.         }
    
  4425.       `,
    
  4426.       // This is a separate warning unrelated to others.
    
  4427.       // We could've made a separate rule for it but it's rare enough to name it.
    
  4428.       // No suggestions because the intent isn't clear.
    
  4429.       errors: [
    
  4430.         {
    
  4431.           message:
    
  4432.             // value2
    
  4433.             `Assignments to the 'value2' variable from inside React Hook useEffect ` +
    
  4434.             `will be lost after each render. To preserve the value over time, ` +
    
  4435.             `store it in a useRef Hook and keep the mutable value in the '.current' property. ` +
    
  4436.             `Otherwise, you can move this variable directly inside useEffect.`,
    
  4437.           suggestions: undefined,
    
  4438.         },
    
  4439.         {
    
  4440.           message:
    
  4441.             // value
    
  4442.             `Assignments to the 'value' variable from inside React Hook useEffect ` +
    
  4443.             `will be lost after each render. To preserve the value over time, ` +
    
  4444.             `store it in a useRef Hook and keep the mutable value in the '.current' property. ` +
    
  4445.             `Otherwise, you can move this variable directly inside useEffect.`,
    
  4446.           suggestions: undefined,
    
  4447.         },
    
  4448.         {
    
  4449.           message:
    
  4450.             // value4
    
  4451.             `Assignments to the 'value4' variable from inside React Hook useEffect ` +
    
  4452.             `will be lost after each render. To preserve the value over time, ` +
    
  4453.             `store it in a useRef Hook and keep the mutable value in the '.current' property. ` +
    
  4454.             `Otherwise, you can move this variable directly inside useEffect.`,
    
  4455.           suggestions: undefined,
    
  4456.         },
    
  4457.         {
    
  4458.           message:
    
  4459.             // asyncValue
    
  4460.             `Assignments to the 'asyncValue' variable from inside React Hook useEffect ` +
    
  4461.             `will be lost after each render. To preserve the value over time, ` +
    
  4462.             `store it in a useRef Hook and keep the mutable value in the '.current' property. ` +
    
  4463.             `Otherwise, you can move this variable directly inside useEffect.`,
    
  4464.           suggestions: undefined,
    
  4465.         },
    
  4466.       ],
    
  4467.     },
    
  4468.     {
    
  4469.       code: normalizeIndent`
    
  4470.         function MyComponent(props) {
    
  4471.           let value;
    
  4472.           let value2;
    
  4473.           let value3;
    
  4474.           let asyncValue;
    
  4475.           useEffect(() => {
    
  4476.             value = {};
    
  4477.             value2 = 100;
    
  4478.             value = 43;
    
  4479.             console.log(value2);
    
  4480.             console.log(value3);
    
  4481.             setTimeout(() => {
    
  4482.               asyncValue = 100;
    
  4483.             });
    
  4484.           }, [value, value2, value3]);
    
  4485.         }
    
  4486.       `,
    
  4487.       // This is a separate warning unrelated to others.
    
  4488.       // We could've made a separate rule for it but it's rare enough to name it.
    
  4489.       // No suggestions because the intent isn't clear.
    
  4490.       errors: [
    
  4491.         {
    
  4492.           message:
    
  4493.             // value
    
  4494.             `Assignments to the 'value' variable from inside React Hook useEffect ` +
    
  4495.             `will be lost after each render. To preserve the value over time, ` +
    
  4496.             `store it in a useRef Hook and keep the mutable value in the '.current' property. ` +
    
  4497.             `Otherwise, you can move this variable directly inside useEffect.`,
    
  4498.           suggestions: undefined,
    
  4499.         },
    
  4500.         {
    
  4501.           message:
    
  4502.             // value2
    
  4503.             `Assignments to the 'value2' variable from inside React Hook useEffect ` +
    
  4504.             `will be lost after each render. To preserve the value over time, ` +
    
  4505.             `store it in a useRef Hook and keep the mutable value in the '.current' property. ` +
    
  4506.             `Otherwise, you can move this variable directly inside useEffect.`,
    
  4507.           suggestions: undefined,
    
  4508.         },
    
  4509.         {
    
  4510.           message:
    
  4511.             // asyncValue
    
  4512.             `Assignments to the 'asyncValue' variable from inside React Hook useEffect ` +
    
  4513.             `will be lost after each render. To preserve the value over time, ` +
    
  4514.             `store it in a useRef Hook and keep the mutable value in the '.current' property. ` +
    
  4515.             `Otherwise, you can move this variable directly inside useEffect.`,
    
  4516.           suggestions: undefined,
    
  4517.         },
    
  4518.       ],
    
  4519.     },
    
  4520.     {
    
  4521.       code: normalizeIndent`
    
  4522.         function MyComponent() {
    
  4523.           const myRef = useRef();
    
  4524.           useEffect(() => {
    
  4525.             const handleMove = () => {};
    
  4526.             myRef.current.addEventListener('mousemove', handleMove);
    
  4527.             return () => myRef.current.removeEventListener('mousemove', handleMove);
    
  4528.           }, []);
    
  4529.           return <div ref={myRef} />;
    
  4530.         }
    
  4531.       `,
    
  4532.       errors: [
    
  4533.         {
    
  4534.           message:
    
  4535.             `The ref value 'myRef.current' will likely have changed by the time ` +
    
  4536.             `this effect cleanup function runs. If this ref points to a node ` +
    
  4537.             `rendered by React, copy 'myRef.current' to a variable inside the effect, ` +
    
  4538.             `and use that variable in the cleanup function.`,
    
  4539.           suggestions: undefined,
    
  4540.         },
    
  4541.       ],
    
  4542.     },
    
  4543.     {
    
  4544.       code: normalizeIndent`
    
  4545.         function MyComponent() {
    
  4546.           const myRef = useRef();
    
  4547.           useEffect(() => {
    
  4548.             const handleMove = () => {};
    
  4549.             myRef?.current?.addEventListener('mousemove', handleMove);
    
  4550.             return () => myRef?.current?.removeEventListener('mousemove', handleMove);
    
  4551.           }, []);
    
  4552.           return <div ref={myRef} />;
    
  4553.         }
    
  4554.       `,
    
  4555.       errors: [
    
  4556.         {
    
  4557.           message:
    
  4558.             `The ref value 'myRef.current' will likely have changed by the time ` +
    
  4559.             `this effect cleanup function runs. If this ref points to a node ` +
    
  4560.             `rendered by React, copy 'myRef.current' to a variable inside the effect, ` +
    
  4561.             `and use that variable in the cleanup function.`,
    
  4562.           suggestions: undefined,
    
  4563.         },
    
  4564.       ],
    
  4565.     },
    
  4566.     {
    
  4567.       code: normalizeIndent`
    
  4568.         function MyComponent() {
    
  4569.           const myRef = useRef();
    
  4570.           useEffect(() => {
    
  4571.             const handleMove = () => {};
    
  4572.             myRef.current.addEventListener('mousemove', handleMove);
    
  4573.             return () => myRef.current.removeEventListener('mousemove', handleMove);
    
  4574.           });
    
  4575.           return <div ref={myRef} />;
    
  4576.         }
    
  4577.       `,
    
  4578.       errors: [
    
  4579.         {
    
  4580.           message:
    
  4581.             `The ref value 'myRef.current' will likely have changed by the time ` +
    
  4582.             `this effect cleanup function runs. If this ref points to a node ` +
    
  4583.             `rendered by React, copy 'myRef.current' to a variable inside the effect, ` +
    
  4584.             `and use that variable in the cleanup function.`,
    
  4585.           suggestions: undefined,
    
  4586.         },
    
  4587.       ],
    
  4588.     },
    
  4589.     {
    
  4590.       code: normalizeIndent`
    
  4591.         function useMyThing(myRef) {
    
  4592.           useEffect(() => {
    
  4593.             const handleMove = () => {};
    
  4594.             myRef.current.addEventListener('mousemove', handleMove);
    
  4595.             return () => myRef.current.removeEventListener('mousemove', handleMove);
    
  4596.           }, [myRef]);
    
  4597.         }
    
  4598.       `,
    
  4599.       errors: [
    
  4600.         {
    
  4601.           message:
    
  4602.             `The ref value 'myRef.current' will likely have changed by the time ` +
    
  4603.             `this effect cleanup function runs. If this ref points to a node ` +
    
  4604.             `rendered by React, copy 'myRef.current' to a variable inside the effect, ` +
    
  4605.             `and use that variable in the cleanup function.`,
    
  4606.           suggestions: undefined,
    
  4607.         },
    
  4608.       ],
    
  4609.     },
    
  4610.     {
    
  4611.       code: normalizeIndent`
    
  4612.         function useMyThing(myRef) {
    
  4613.           useEffect(() => {
    
  4614.             const handleMouse = () => {};
    
  4615.             myRef.current.addEventListener('mousemove', handleMouse);
    
  4616.             myRef.current.addEventListener('mousein', handleMouse);
    
  4617.             return function() {
    
  4618.               setTimeout(() => {
    
  4619.                 myRef.current.removeEventListener('mousemove', handleMouse);
    
  4620.                 myRef.current.removeEventListener('mousein', handleMouse);
    
  4621.               });
    
  4622.             }
    
  4623.           }, [myRef]);
    
  4624.         }
    
  4625.       `,
    
  4626.       errors: [
    
  4627.         {
    
  4628.           message:
    
  4629.             `The ref value 'myRef.current' will likely have changed by the time ` +
    
  4630.             `this effect cleanup function runs. If this ref points to a node ` +
    
  4631.             `rendered by React, copy 'myRef.current' to a variable inside the effect, ` +
    
  4632.             `and use that variable in the cleanup function.`,
    
  4633.           suggestions: undefined,
    
  4634.         },
    
  4635.       ],
    
  4636.     },
    
  4637.     {
    
  4638.       code: normalizeIndent`
    
  4639.         function useMyThing(myRef, active) {
    
  4640.           useEffect(() => {
    
  4641.             const handleMove = () => {};
    
  4642.             if (active) {
    
  4643.               myRef.current.addEventListener('mousemove', handleMove);
    
  4644.               return function() {
    
  4645.                 setTimeout(() => {
    
  4646.                   myRef.current.removeEventListener('mousemove', handleMove);
    
  4647.                 });
    
  4648.               }
    
  4649.             }
    
  4650.           }, [myRef, active]);
    
  4651.         }
    
  4652.       `,
    
  4653.       errors: [
    
  4654.         {
    
  4655.           message:
    
  4656.             `The ref value 'myRef.current' will likely have changed by the time ` +
    
  4657.             `this effect cleanup function runs. If this ref points to a node ` +
    
  4658.             `rendered by React, copy 'myRef.current' to a variable inside the effect, ` +
    
  4659.             `and use that variable in the cleanup function.`,
    
  4660.           suggestions: undefined,
    
  4661.         },
    
  4662.       ],
    
  4663.     },
    
  4664.     {
    
  4665.       code: `
    
  4666.         function MyComponent() {
    
  4667.           const myRef = useRef();
    
  4668.           useLayoutEffect_SAFE_FOR_SSR(() => {
    
  4669.             const handleMove = () => {};
    
  4670.             myRef.current.addEventListener('mousemove', handleMove);
    
  4671.             return () => myRef.current.removeEventListener('mousemove', handleMove);
    
  4672.           });
    
  4673.           return <div ref={myRef} />;
    
  4674.         }
    
  4675.       `,
    
  4676.       output: `
    
  4677.         function MyComponent() {
    
  4678.           const myRef = useRef();
    
  4679.           useLayoutEffect_SAFE_FOR_SSR(() => {
    
  4680.             const handleMove = () => {};
    
  4681.             myRef.current.addEventListener('mousemove', handleMove);
    
  4682.             return () => myRef.current.removeEventListener('mousemove', handleMove);
    
  4683.           });
    
  4684.           return <div ref={myRef} />;
    
  4685.         }
    
  4686.       `,
    
  4687.       errors: [
    
  4688.         `The ref value 'myRef.current' will likely have changed by the time ` +
    
  4689.           `this effect cleanup function runs. If this ref points to a node ` +
    
  4690.           `rendered by React, copy 'myRef.current' to a variable inside the effect, ` +
    
  4691.           `and use that variable in the cleanup function.`,
    
  4692.       ],
    
  4693.       options: [{additionalHooks: 'useLayoutEffect_SAFE_FOR_SSR'}],
    
  4694.     },
    
  4695.     {
    
  4696.       // Autofix ignores constant primitives (leaving the ones that are there).
    
  4697.       code: normalizeIndent`
    
  4698.         function MyComponent() {
    
  4699.           const local1 = 42;
    
  4700.           const local2 = '42';
    
  4701.           const local3 = null;
    
  4702.           const local4 = {};
    
  4703.           useEffect(() => {
    
  4704.             console.log(local1);
    
  4705.             console.log(local2);
    
  4706.             console.log(local3);
    
  4707.             console.log(local4);
    
  4708.           }, [local1, local3]);
    
  4709.         }
    
  4710.       `,
    
  4711.       errors: [
    
  4712.         {
    
  4713.           message:
    
  4714.             "React Hook useEffect has a missing dependency: 'local4'. " +
    
  4715.             'Either include it or remove the dependency array.',
    
  4716.           suggestions: [
    
  4717.             {
    
  4718.               desc: 'Update the dependencies array to be: [local1, local3, local4]',
    
  4719.               output: normalizeIndent`
    
  4720.                 function MyComponent() {
    
  4721.                   const local1 = 42;
    
  4722.                   const local2 = '42';
    
  4723.                   const local3 = null;
    
  4724.                   const local4 = {};
    
  4725.                   useEffect(() => {
    
  4726.                     console.log(local1);
    
  4727.                     console.log(local2);
    
  4728.                     console.log(local3);
    
  4729.                     console.log(local4);
    
  4730.                   }, [local1, local3, local4]);
    
  4731.                 }
    
  4732.               `,
    
  4733.             },
    
  4734.           ],
    
  4735.         },
    
  4736.       ],
    
  4737.     },
    
  4738.     {
    
  4739.       code: normalizeIndent`
    
  4740.         function MyComponent() {
    
  4741.           useEffect(() => {
    
  4742.             window.scrollTo(0, 0);
    
  4743.           }, [window]);
    
  4744.         }
    
  4745.       `,
    
  4746.       errors: [
    
  4747.         {
    
  4748.           message:
    
  4749.             "React Hook useEffect has an unnecessary dependency: 'window'. " +
    
  4750.             'Either exclude it or remove the dependency array. ' +
    
  4751.             "Outer scope values like 'window' aren't valid dependencies " +
    
  4752.             "because mutating them doesn't re-render the component.",
    
  4753.           suggestions: [
    
  4754.             {
    
  4755.               desc: 'Update the dependencies array to be: []',
    
  4756.               output: normalizeIndent`
    
  4757.                 function MyComponent() {
    
  4758.                   useEffect(() => {
    
  4759.                     window.scrollTo(0, 0);
    
  4760.                   }, []);
    
  4761.                 }
    
  4762.               `,
    
  4763.             },
    
  4764.           ],
    
  4765.         },
    
  4766.       ],
    
  4767.     },
    
  4768.     {
    
  4769.       code: normalizeIndent`
    
  4770.         import MutableStore from 'store';
    
  4771. 
    
  4772.         function MyComponent() {
    
  4773.           useEffect(() => {
    
  4774.             console.log(MutableStore.hello);
    
  4775.           }, [MutableStore.hello]);
    
  4776.         }
    
  4777.       `,
    
  4778.       errors: [
    
  4779.         {
    
  4780.           message:
    
  4781.             "React Hook useEffect has an unnecessary dependency: 'MutableStore.hello'. " +
    
  4782.             'Either exclude it or remove the dependency array. ' +
    
  4783.             "Outer scope values like 'MutableStore.hello' aren't valid dependencies " +
    
  4784.             "because mutating them doesn't re-render the component.",
    
  4785.           suggestions: [
    
  4786.             {
    
  4787.               desc: 'Update the dependencies array to be: []',
    
  4788.               output: normalizeIndent`
    
  4789.                 import MutableStore from 'store';
    
  4790. 
    
  4791.                 function MyComponent() {
    
  4792.                   useEffect(() => {
    
  4793.                     console.log(MutableStore.hello);
    
  4794.                   }, []);
    
  4795.                 }
    
  4796.               `,
    
  4797.             },
    
  4798.           ],
    
  4799.         },
    
  4800.       ],
    
  4801.     },
    
  4802.     {
    
  4803.       code: normalizeIndent`
    
  4804.         import MutableStore from 'store';
    
  4805.         let z = {};
    
  4806. 
    
  4807.         function MyComponent(props) {
    
  4808.           let x = props.foo;
    
  4809.           {
    
  4810.             let y = props.bar;
    
  4811.             useEffect(() => {
    
  4812.               console.log(MutableStore.hello.world, props.foo, x, y, z, global.stuff);
    
  4813.             }, [MutableStore.hello.world, props.foo, x, y, z, global.stuff]);
    
  4814.           }
    
  4815.         }
    
  4816.       `,
    
  4817.       errors: [
    
  4818.         {
    
  4819.           message:
    
  4820.             'React Hook useEffect has unnecessary dependencies: ' +
    
  4821.             "'MutableStore.hello.world', 'global.stuff', and 'z'. " +
    
  4822.             'Either exclude them or remove the dependency array. ' +
    
  4823.             "Outer scope values like 'MutableStore.hello.world' aren't valid dependencies " +
    
  4824.             "because mutating them doesn't re-render the component.",
    
  4825.           suggestions: [
    
  4826.             {
    
  4827.               desc: 'Update the dependencies array to be: [props.foo, x, y]',
    
  4828.               output: normalizeIndent`
    
  4829.                 import MutableStore from 'store';
    
  4830.                 let z = {};
    
  4831. 
    
  4832.                 function MyComponent(props) {
    
  4833.                   let x = props.foo;
    
  4834.                   {
    
  4835.                     let y = props.bar;
    
  4836.                     useEffect(() => {
    
  4837.                       console.log(MutableStore.hello.world, props.foo, x, y, z, global.stuff);
    
  4838.                     }, [props.foo, x, y]);
    
  4839.                   }
    
  4840.                 }
    
  4841.               `,
    
  4842.             },
    
  4843.           ],
    
  4844.         },
    
  4845.       ],
    
  4846.     },
    
  4847.     {
    
  4848.       code: normalizeIndent`
    
  4849.         import MutableStore from 'store';
    
  4850.         let z = {};
    
  4851. 
    
  4852.         function MyComponent(props) {
    
  4853.           let x = props.foo;
    
  4854.           {
    
  4855.             let y = props.bar;
    
  4856.             useEffect(() => {
    
  4857.               // nothing
    
  4858.             }, [MutableStore.hello.world, props.foo, x, y, z, global.stuff]);
    
  4859.           }
    
  4860.         }
    
  4861.       `,
    
  4862.       errors: [
    
  4863.         {
    
  4864.           message:
    
  4865.             'React Hook useEffect has unnecessary dependencies: ' +
    
  4866.             "'MutableStore.hello.world', 'global.stuff', and 'z'. " +
    
  4867.             'Either exclude them or remove the dependency array. ' +
    
  4868.             "Outer scope values like 'MutableStore.hello.world' aren't valid dependencies " +
    
  4869.             "because mutating them doesn't re-render the component.",
    
  4870.           // The output should contain the ones that are inside a component
    
  4871.           // since there are legit reasons to over-specify them for effects.
    
  4872.           suggestions: [
    
  4873.             {
    
  4874.               desc: 'Update the dependencies array to be: [props.foo, x, y]',
    
  4875.               output: normalizeIndent`
    
  4876.                 import MutableStore from 'store';
    
  4877.                 let z = {};
    
  4878. 
    
  4879.                 function MyComponent(props) {
    
  4880.                   let x = props.foo;
    
  4881.                   {
    
  4882.                     let y = props.bar;
    
  4883.                     useEffect(() => {
    
  4884.                       // nothing
    
  4885.                     }, [props.foo, x, y]);
    
  4886.                   }
    
  4887.                 }
    
  4888.               `,
    
  4889.             },
    
  4890.           ],
    
  4891.         },
    
  4892.       ],
    
  4893.     },
    
  4894.     {
    
  4895.       code: normalizeIndent`
    
  4896.         import MutableStore from 'store';
    
  4897.         let z = {};
    
  4898. 
    
  4899.         function MyComponent(props) {
    
  4900.           let x = props.foo;
    
  4901.           {
    
  4902.             let y = props.bar;
    
  4903.             const fn = useCallback(() => {
    
  4904.               // nothing
    
  4905.             }, [MutableStore.hello.world, props.foo, x, y, z, global.stuff]);
    
  4906.           }
    
  4907.         }
    
  4908.       `,
    
  4909.       errors: [
    
  4910.         {
    
  4911.           message:
    
  4912.             'React Hook useCallback has unnecessary dependencies: ' +
    
  4913.             "'MutableStore.hello.world', 'global.stuff', 'props.foo', 'x', 'y', and 'z'. " +
    
  4914.             'Either exclude them or remove the dependency array. ' +
    
  4915.             "Outer scope values like 'MutableStore.hello.world' aren't valid dependencies " +
    
  4916.             "because mutating them doesn't re-render the component.",
    
  4917.           suggestions: [
    
  4918.             {
    
  4919.               desc: 'Update the dependencies array to be: []',
    
  4920.               output: normalizeIndent`
    
  4921.                 import MutableStore from 'store';
    
  4922.                 let z = {};
    
  4923. 
    
  4924.                 function MyComponent(props) {
    
  4925.                   let x = props.foo;
    
  4926.                   {
    
  4927.                     let y = props.bar;
    
  4928.                     const fn = useCallback(() => {
    
  4929.                       // nothing
    
  4930.                     }, []);
    
  4931.                   }
    
  4932.                 }
    
  4933.               `,
    
  4934.             },
    
  4935.           ],
    
  4936.         },
    
  4937.       ],
    
  4938.     },
    
  4939.     {
    
  4940.       code: normalizeIndent`
    
  4941.         import MutableStore from 'store';
    
  4942.         let z = {};
    
  4943. 
    
  4944.         function MyComponent(props) {
    
  4945.           let x = props.foo;
    
  4946.           {
    
  4947.             let y = props.bar;
    
  4948.             const fn = useCallback(() => {
    
  4949.               // nothing
    
  4950.             }, [MutableStore?.hello?.world, props.foo, x, y, z, global?.stuff]);
    
  4951.           }
    
  4952.         }
    
  4953.       `,
    
  4954.       errors: [
    
  4955.         {
    
  4956.           message:
    
  4957.             'React Hook useCallback has unnecessary dependencies: ' +
    
  4958.             "'MutableStore.hello.world', 'global.stuff', 'props.foo', 'x', 'y', and 'z'. " +
    
  4959.             'Either exclude them or remove the dependency array. ' +
    
  4960.             "Outer scope values like 'MutableStore.hello.world' aren't valid dependencies " +
    
  4961.             "because mutating them doesn't re-render the component.",
    
  4962.           suggestions: [
    
  4963.             {
    
  4964.               desc: 'Update the dependencies array to be: []',
    
  4965.               output: normalizeIndent`
    
  4966.                 import MutableStore from 'store';
    
  4967.                 let z = {};
    
  4968. 
    
  4969.                 function MyComponent(props) {
    
  4970.                   let x = props.foo;
    
  4971.                   {
    
  4972.                     let y = props.bar;
    
  4973.                     const fn = useCallback(() => {
    
  4974.                       // nothing
    
  4975.                     }, []);
    
  4976.                   }
    
  4977.                 }
    
  4978.               `,
    
  4979.             },
    
  4980.           ],
    
  4981.         },
    
  4982.       ],
    
  4983.     },
    
  4984.     {
    
  4985.       // Every almost-static function is tainted by a dynamic value.
    
  4986.       code: normalizeIndent`
    
  4987.         function MyComponent(props) {
    
  4988.           let [, setState] = useState();
    
  4989.           let [, dispatch] = React.useReducer();
    
  4990.           let taint = props.foo;
    
  4991. 
    
  4992.           function handleNext1(value) {
    
  4993.             let value2 = value * taint;
    
  4994.             setState(value2);
    
  4995.             console.log('hello');
    
  4996.           }
    
  4997.           const handleNext2 = (value) => {
    
  4998.             setState(taint(value));
    
  4999.             console.log('hello');
    
  5000.           };
    
  5001.           let handleNext3 = function(value) {
    
  5002.             setTimeout(() => console.log(taint));
    
  5003.             dispatch({ type: 'x', value });
    
  5004.           };
    
  5005.           useEffect(() => {
    
  5006.             return Store.subscribe(handleNext1);
    
  5007.           }, []);
    
  5008.           useLayoutEffect(() => {
    
  5009.             return Store.subscribe(handleNext2);
    
  5010.           }, []);
    
  5011.           useMemo(() => {
    
  5012.             return Store.subscribe(handleNext3);
    
  5013.           }, []);
    
  5014.         }
    
  5015.       `,
    
  5016.       errors: [
    
  5017.         {
    
  5018.           message:
    
  5019.             "React Hook useEffect has a missing dependency: 'handleNext1'. " +
    
  5020.             'Either include it or remove the dependency array.',
    
  5021.           suggestions: [
    
  5022.             {
    
  5023.               desc: 'Update the dependencies array to be: [handleNext1]',
    
  5024.               output: normalizeIndent`
    
  5025.                 function MyComponent(props) {
    
  5026.                   let [, setState] = useState();
    
  5027.                   let [, dispatch] = React.useReducer();
    
  5028.                   let taint = props.foo;
    
  5029. 
    
  5030.                   function handleNext1(value) {
    
  5031.                     let value2 = value * taint;
    
  5032.                     setState(value2);
    
  5033.                     console.log('hello');
    
  5034.                   }
    
  5035.                   const handleNext2 = (value) => {
    
  5036.                     setState(taint(value));
    
  5037.                     console.log('hello');
    
  5038.                   };
    
  5039.                   let handleNext3 = function(value) {
    
  5040.                     setTimeout(() => console.log(taint));
    
  5041.                     dispatch({ type: 'x', value });
    
  5042.                   };
    
  5043.                   useEffect(() => {
    
  5044.                     return Store.subscribe(handleNext1);
    
  5045.                   }, [handleNext1]);
    
  5046.                   useLayoutEffect(() => {
    
  5047.                     return Store.subscribe(handleNext2);
    
  5048.                   }, []);
    
  5049.                   useMemo(() => {
    
  5050.                     return Store.subscribe(handleNext3);
    
  5051.                   }, []);
    
  5052.                 }
    
  5053.               `,
    
  5054.             },
    
  5055.           ],
    
  5056.         },
    
  5057.         {
    
  5058.           message:
    
  5059.             "React Hook useLayoutEffect has a missing dependency: 'handleNext2'. " +
    
  5060.             'Either include it or remove the dependency array.',
    
  5061.           suggestions: [
    
  5062.             {
    
  5063.               desc: 'Update the dependencies array to be: [handleNext2]',
    
  5064.               output: normalizeIndent`
    
  5065.                 function MyComponent(props) {
    
  5066.                   let [, setState] = useState();
    
  5067.                   let [, dispatch] = React.useReducer();
    
  5068.                   let taint = props.foo;
    
  5069. 
    
  5070.                   function handleNext1(value) {
    
  5071.                     let value2 = value * taint;
    
  5072.                     setState(value2);
    
  5073.                     console.log('hello');
    
  5074.                   }
    
  5075.                   const handleNext2 = (value) => {
    
  5076.                     setState(taint(value));
    
  5077.                     console.log('hello');
    
  5078.                   };
    
  5079.                   let handleNext3 = function(value) {
    
  5080.                     setTimeout(() => console.log(taint));
    
  5081.                     dispatch({ type: 'x', value });
    
  5082.                   };
    
  5083.                   useEffect(() => {
    
  5084.                     return Store.subscribe(handleNext1);
    
  5085.                   }, []);
    
  5086.                   useLayoutEffect(() => {
    
  5087.                     return Store.subscribe(handleNext2);
    
  5088.                   }, [handleNext2]);
    
  5089.                   useMemo(() => {
    
  5090.                     return Store.subscribe(handleNext3);
    
  5091.                   }, []);
    
  5092.                 }
    
  5093.               `,
    
  5094.             },
    
  5095.           ],
    
  5096.         },
    
  5097.         {
    
  5098.           message:
    
  5099.             "React Hook useMemo has a missing dependency: 'handleNext3'. " +
    
  5100.             'Either include it or remove the dependency array.',
    
  5101.           suggestions: [
    
  5102.             {
    
  5103.               desc: 'Update the dependencies array to be: [handleNext3]',
    
  5104.               output: normalizeIndent`
    
  5105.                 function MyComponent(props) {
    
  5106.                   let [, setState] = useState();
    
  5107.                   let [, dispatch] = React.useReducer();
    
  5108.                   let taint = props.foo;
    
  5109. 
    
  5110.                   function handleNext1(value) {
    
  5111.                     let value2 = value * taint;
    
  5112.                     setState(value2);
    
  5113.                     console.log('hello');
    
  5114.                   }
    
  5115.                   const handleNext2 = (value) => {
    
  5116.                     setState(taint(value));
    
  5117.                     console.log('hello');
    
  5118.                   };
    
  5119.                   let handleNext3 = function(value) {
    
  5120.                     setTimeout(() => console.log(taint));
    
  5121.                     dispatch({ type: 'x', value });
    
  5122.                   };
    
  5123.                   useEffect(() => {
    
  5124.                     return Store.subscribe(handleNext1);
    
  5125.                   }, []);
    
  5126.                   useLayoutEffect(() => {
    
  5127.                     return Store.subscribe(handleNext2);
    
  5128.                   }, []);
    
  5129.                   useMemo(() => {
    
  5130.                     return Store.subscribe(handleNext3);
    
  5131.                   }, [handleNext3]);
    
  5132.                 }
    
  5133.               `,
    
  5134.             },
    
  5135.           ],
    
  5136.         },
    
  5137.       ],
    
  5138.     },
    
  5139.     {
    
  5140.       // Regression test
    
  5141.       code: normalizeIndent`
    
  5142.         function MyComponent(props) {
    
  5143.           let [, setState] = useState();
    
  5144.           let [, dispatch] = React.useReducer();
    
  5145.           let taint = props.foo;
    
  5146. 
    
  5147.           // Shouldn't affect anything
    
  5148.           function handleChange() {}
    
  5149. 
    
  5150.           function handleNext1(value) {
    
  5151.             let value2 = value * taint;
    
  5152.             setState(value2);
    
  5153.             console.log('hello');
    
  5154.           }
    
  5155.           const handleNext2 = (value) => {
    
  5156.             setState(taint(value));
    
  5157.             console.log('hello');
    
  5158.           };
    
  5159.           let handleNext3 = function(value) {
    
  5160.             console.log(taint);
    
  5161.             dispatch({ type: 'x', value });
    
  5162.           };
    
  5163.           useEffect(() => {
    
  5164.             return Store.subscribe(handleNext1);
    
  5165.           }, []);
    
  5166.           useLayoutEffect(() => {
    
  5167.             return Store.subscribe(handleNext2);
    
  5168.           }, []);
    
  5169.           useMemo(() => {
    
  5170.             return Store.subscribe(handleNext3);
    
  5171.           }, []);
    
  5172.         }
    
  5173.       `,
    
  5174.       errors: [
    
  5175.         {
    
  5176.           message:
    
  5177.             "React Hook useEffect has a missing dependency: 'handleNext1'. " +
    
  5178.             'Either include it or remove the dependency array.',
    
  5179.           suggestions: [
    
  5180.             {
    
  5181.               desc: 'Update the dependencies array to be: [handleNext1]',
    
  5182.               output: normalizeIndent`
    
  5183.                 function MyComponent(props) {
    
  5184.                   let [, setState] = useState();
    
  5185.                   let [, dispatch] = React.useReducer();
    
  5186.                   let taint = props.foo;
    
  5187. 
    
  5188.                   // Shouldn't affect anything
    
  5189.                   function handleChange() {}
    
  5190. 
    
  5191.                   function handleNext1(value) {
    
  5192.                     let value2 = value * taint;
    
  5193.                     setState(value2);
    
  5194.                     console.log('hello');
    
  5195.                   }
    
  5196.                   const handleNext2 = (value) => {
    
  5197.                     setState(taint(value));
    
  5198.                     console.log('hello');
    
  5199.                   };
    
  5200.                   let handleNext3 = function(value) {
    
  5201.                     console.log(taint);
    
  5202.                     dispatch({ type: 'x', value });
    
  5203.                   };
    
  5204.                   useEffect(() => {
    
  5205.                     return Store.subscribe(handleNext1);
    
  5206.                   }, [handleNext1]);
    
  5207.                   useLayoutEffect(() => {
    
  5208.                     return Store.subscribe(handleNext2);
    
  5209.                   }, []);
    
  5210.                   useMemo(() => {
    
  5211.                     return Store.subscribe(handleNext3);
    
  5212.                   }, []);
    
  5213.                 }
    
  5214.               `,
    
  5215.             },
    
  5216.           ],
    
  5217.         },
    
  5218.         {
    
  5219.           message:
    
  5220.             "React Hook useLayoutEffect has a missing dependency: 'handleNext2'. " +
    
  5221.             'Either include it or remove the dependency array.',
    
  5222.           suggestions: [
    
  5223.             {
    
  5224.               desc: 'Update the dependencies array to be: [handleNext2]',
    
  5225.               output: normalizeIndent`
    
  5226.                 function MyComponent(props) {
    
  5227.                   let [, setState] = useState();
    
  5228.                   let [, dispatch] = React.useReducer();
    
  5229.                   let taint = props.foo;
    
  5230. 
    
  5231.                   // Shouldn't affect anything
    
  5232.                   function handleChange() {}
    
  5233. 
    
  5234.                   function handleNext1(value) {
    
  5235.                     let value2 = value * taint;
    
  5236.                     setState(value2);
    
  5237.                     console.log('hello');
    
  5238.                   }
    
  5239.                   const handleNext2 = (value) => {
    
  5240.                     setState(taint(value));
    
  5241.                     console.log('hello');
    
  5242.                   };
    
  5243.                   let handleNext3 = function(value) {
    
  5244.                     console.log(taint);
    
  5245.                     dispatch({ type: 'x', value });
    
  5246.                   };
    
  5247.                   useEffect(() => {
    
  5248.                     return Store.subscribe(handleNext1);
    
  5249.                   }, []);
    
  5250.                   useLayoutEffect(() => {
    
  5251.                     return Store.subscribe(handleNext2);
    
  5252.                   }, [handleNext2]);
    
  5253.                   useMemo(() => {
    
  5254.                     return Store.subscribe(handleNext3);
    
  5255.                   }, []);
    
  5256.                 }
    
  5257.               `,
    
  5258.             },
    
  5259.           ],
    
  5260.         },
    
  5261.         {
    
  5262.           message:
    
  5263.             "React Hook useMemo has a missing dependency: 'handleNext3'. " +
    
  5264.             'Either include it or remove the dependency array.',
    
  5265.           suggestions: [
    
  5266.             {
    
  5267.               desc: 'Update the dependencies array to be: [handleNext3]',
    
  5268.               output: normalizeIndent`
    
  5269.                 function MyComponent(props) {
    
  5270.                   let [, setState] = useState();
    
  5271.                   let [, dispatch] = React.useReducer();
    
  5272.                   let taint = props.foo;
    
  5273. 
    
  5274.                   // Shouldn't affect anything
    
  5275.                   function handleChange() {}
    
  5276. 
    
  5277.                   function handleNext1(value) {
    
  5278.                     let value2 = value * taint;
    
  5279.                     setState(value2);
    
  5280.                     console.log('hello');
    
  5281.                   }
    
  5282.                   const handleNext2 = (value) => {
    
  5283.                     setState(taint(value));
    
  5284.                     console.log('hello');
    
  5285.                   };
    
  5286.                   let handleNext3 = function(value) {
    
  5287.                     console.log(taint);
    
  5288.                     dispatch({ type: 'x', value });
    
  5289.                   };
    
  5290.                   useEffect(() => {
    
  5291.                     return Store.subscribe(handleNext1);
    
  5292.                   }, []);
    
  5293.                   useLayoutEffect(() => {
    
  5294.                     return Store.subscribe(handleNext2);
    
  5295.                   }, []);
    
  5296.                   useMemo(() => {
    
  5297.                     return Store.subscribe(handleNext3);
    
  5298.                   }, [handleNext3]);
    
  5299.                 }
    
  5300.               `,
    
  5301.             },
    
  5302.           ],
    
  5303.         },
    
  5304.       ],
    
  5305.     },
    
  5306.     {
    
  5307.       // Regression test
    
  5308.       code: normalizeIndent`
    
  5309.         function MyComponent(props) {
    
  5310.           let [, setState] = useState();
    
  5311.           let [, dispatch] = React.useReducer();
    
  5312.           let taint = props.foo;
    
  5313. 
    
  5314.           // Shouldn't affect anything
    
  5315.           const handleChange = () => {};
    
  5316. 
    
  5317.           function handleNext1(value) {
    
  5318.             let value2 = value * taint;
    
  5319.             setState(value2);
    
  5320.             console.log('hello');
    
  5321.           }
    
  5322.           const handleNext2 = (value) => {
    
  5323.             setState(taint(value));
    
  5324.             console.log('hello');
    
  5325.           };
    
  5326.           let handleNext3 = function(value) {
    
  5327.             console.log(taint);
    
  5328.             dispatch({ type: 'x', value });
    
  5329.           };
    
  5330.           useEffect(() => {
    
  5331.             return Store.subscribe(handleNext1);
    
  5332.           }, []);
    
  5333.           useLayoutEffect(() => {
    
  5334.             return Store.subscribe(handleNext2);
    
  5335.           }, []);
    
  5336.           useMemo(() => {
    
  5337.             return Store.subscribe(handleNext3);
    
  5338.           }, []);
    
  5339.         }
    
  5340.       `,
    
  5341.       errors: [
    
  5342.         {
    
  5343.           message:
    
  5344.             "React Hook useEffect has a missing dependency: 'handleNext1'. " +
    
  5345.             'Either include it or remove the dependency array.',
    
  5346.           suggestions: [
    
  5347.             {
    
  5348.               desc: 'Update the dependencies array to be: [handleNext1]',
    
  5349.               output: normalizeIndent`
    
  5350.                 function MyComponent(props) {
    
  5351.                   let [, setState] = useState();
    
  5352.                   let [, dispatch] = React.useReducer();
    
  5353.                   let taint = props.foo;
    
  5354. 
    
  5355.                   // Shouldn't affect anything
    
  5356.                   const handleChange = () => {};
    
  5357. 
    
  5358.                   function handleNext1(value) {
    
  5359.                     let value2 = value * taint;
    
  5360.                     setState(value2);
    
  5361.                     console.log('hello');
    
  5362.                   }
    
  5363.                   const handleNext2 = (value) => {
    
  5364.                     setState(taint(value));
    
  5365.                     console.log('hello');
    
  5366.                   };
    
  5367.                   let handleNext3 = function(value) {
    
  5368.                     console.log(taint);
    
  5369.                     dispatch({ type: 'x', value });
    
  5370.                   };
    
  5371.                   useEffect(() => {
    
  5372.                     return Store.subscribe(handleNext1);
    
  5373.                   }, [handleNext1]);
    
  5374.                   useLayoutEffect(() => {
    
  5375.                     return Store.subscribe(handleNext2);
    
  5376.                   }, []);
    
  5377.                   useMemo(() => {
    
  5378.                     return Store.subscribe(handleNext3);
    
  5379.                   }, []);
    
  5380.                 }
    
  5381.               `,
    
  5382.             },
    
  5383.           ],
    
  5384.         },
    
  5385.         {
    
  5386.           message:
    
  5387.             "React Hook useLayoutEffect has a missing dependency: 'handleNext2'. " +
    
  5388.             'Either include it or remove the dependency array.',
    
  5389.           suggestions: [
    
  5390.             {
    
  5391.               desc: 'Update the dependencies array to be: [handleNext2]',
    
  5392.               output: normalizeIndent`
    
  5393.                 function MyComponent(props) {
    
  5394.                   let [, setState] = useState();
    
  5395.                   let [, dispatch] = React.useReducer();
    
  5396.                   let taint = props.foo;
    
  5397. 
    
  5398.                   // Shouldn't affect anything
    
  5399.                   const handleChange = () => {};
    
  5400. 
    
  5401.                   function handleNext1(value) {
    
  5402.                     let value2 = value * taint;
    
  5403.                     setState(value2);
    
  5404.                     console.log('hello');
    
  5405.                   }
    
  5406.                   const handleNext2 = (value) => {
    
  5407.                     setState(taint(value));
    
  5408.                     console.log('hello');
    
  5409.                   };
    
  5410.                   let handleNext3 = function(value) {
    
  5411.                     console.log(taint);
    
  5412.                     dispatch({ type: 'x', value });
    
  5413.                   };
    
  5414.                   useEffect(() => {
    
  5415.                     return Store.subscribe(handleNext1);
    
  5416.                   }, []);
    
  5417.                   useLayoutEffect(() => {
    
  5418.                     return Store.subscribe(handleNext2);
    
  5419.                   }, [handleNext2]);
    
  5420.                   useMemo(() => {
    
  5421.                     return Store.subscribe(handleNext3);
    
  5422.                   }, []);
    
  5423.                 }
    
  5424.               `,
    
  5425.             },
    
  5426.           ],
    
  5427.         },
    
  5428.         {
    
  5429.           message:
    
  5430.             "React Hook useMemo has a missing dependency: 'handleNext3'. " +
    
  5431.             'Either include it or remove the dependency array.',
    
  5432.           suggestions: [
    
  5433.             {
    
  5434.               desc: 'Update the dependencies array to be: [handleNext3]',
    
  5435.               output: normalizeIndent`
    
  5436.                 function MyComponent(props) {
    
  5437.                   let [, setState] = useState();
    
  5438.                   let [, dispatch] = React.useReducer();
    
  5439.                   let taint = props.foo;
    
  5440. 
    
  5441.                   // Shouldn't affect anything
    
  5442.                   const handleChange = () => {};
    
  5443. 
    
  5444.                   function handleNext1(value) {
    
  5445.                     let value2 = value * taint;
    
  5446.                     setState(value2);
    
  5447.                     console.log('hello');
    
  5448.                   }
    
  5449.                   const handleNext2 = (value) => {
    
  5450.                     setState(taint(value));
    
  5451.                     console.log('hello');
    
  5452.                   };
    
  5453.                   let handleNext3 = function(value) {
    
  5454.                     console.log(taint);
    
  5455.                     dispatch({ type: 'x', value });
    
  5456.                   };
    
  5457.                   useEffect(() => {
    
  5458.                     return Store.subscribe(handleNext1);
    
  5459.                   }, []);
    
  5460.                   useLayoutEffect(() => {
    
  5461.                     return Store.subscribe(handleNext2);
    
  5462.                   }, []);
    
  5463.                   useMemo(() => {
    
  5464.                     return Store.subscribe(handleNext3);
    
  5465.                   }, [handleNext3]);
    
  5466.                 }
    
  5467.               `,
    
  5468.             },
    
  5469.           ],
    
  5470.         },
    
  5471.       ],
    
  5472.     },
    
  5473.     {
    
  5474.       code: normalizeIndent`
    
  5475.         function MyComponent(props) {
    
  5476.           let [, setState] = useState();
    
  5477. 
    
  5478.           function handleNext(value) {
    
  5479.             setState(value);
    
  5480.           }
    
  5481. 
    
  5482.           useEffect(() => {
    
  5483.             return Store.subscribe(handleNext);
    
  5484.           }, [handleNext]);
    
  5485.         }
    
  5486.       `,
    
  5487.       errors: [
    
  5488.         {
    
  5489.           message:
    
  5490.             `The 'handleNext' function makes the dependencies of ` +
    
  5491.             `useEffect Hook (at line 11) change on every render. ` +
    
  5492.             `Move it inside the useEffect callback. Alternatively, ` +
    
  5493.             `wrap the definition of 'handleNext' in its own useCallback() Hook.`,
    
  5494.           // Not gonna fix a function definition
    
  5495.           // because it's not always safe due to hoisting.
    
  5496.           suggestions: undefined,
    
  5497.         },
    
  5498.       ],
    
  5499.     },
    
  5500.     {
    
  5501.       // Even if the function only references static values,
    
  5502.       // once you specify it in deps, it will invalidate them.
    
  5503.       code: normalizeIndent`
    
  5504.         function MyComponent(props) {
    
  5505.           let [, setState] = useState();
    
  5506. 
    
  5507.           const handleNext = (value) => {
    
  5508.             setState(value);
    
  5509.           };
    
  5510. 
    
  5511.           useEffect(() => {
    
  5512.             return Store.subscribe(handleNext);
    
  5513.           }, [handleNext]);
    
  5514.         }
    
  5515.       `,
    
  5516.       errors: [
    
  5517.         {
    
  5518.           message:
    
  5519.             `The 'handleNext' function makes the dependencies of ` +
    
  5520.             `useEffect Hook (at line 11) change on every render. ` +
    
  5521.             `Move it inside the useEffect callback. Alternatively, ` +
    
  5522.             `wrap the definition of 'handleNext' in its own useCallback() Hook.`,
    
  5523.           // We don't fix moving (too invasive). But that's the suggested fix
    
  5524.           // when only effect uses this function. Otherwise, we'd useCallback.
    
  5525.           suggestions: undefined,
    
  5526.         },
    
  5527.       ],
    
  5528.     },
    
  5529.     {
    
  5530.       // Even if the function only references static values,
    
  5531.       // once you specify it in deps, it will invalidate them.
    
  5532.       // However, we can't suggest moving handleNext into the
    
  5533.       // effect because it is *also* used outside of it.
    
  5534.       // So our suggestion is useCallback().
    
  5535.       code: normalizeIndent`
    
  5536.         function MyComponent(props) {
    
  5537.           let [, setState] = useState();
    
  5538. 
    
  5539.           const handleNext = (value) => {
    
  5540.             setState(value);
    
  5541.           };
    
  5542. 
    
  5543.           useEffect(() => {
    
  5544.             return Store.subscribe(handleNext);
    
  5545.           }, [handleNext]);
    
  5546. 
    
  5547.           return <div onClick={handleNext} />;
    
  5548.         }
    
  5549.       `,
    
  5550.       errors: [
    
  5551.         {
    
  5552.           message:
    
  5553.             `The 'handleNext' function makes the dependencies of ` +
    
  5554.             `useEffect Hook (at line 11) change on every render. ` +
    
  5555.             `To fix this, wrap the definition of 'handleNext' in its own useCallback() Hook.`,
    
  5556.           // We fix this one with useCallback since it's
    
  5557.           // the easy fix and you can't just move it into effect.
    
  5558.           suggestions: [
    
  5559.             {
    
  5560.               desc: "Wrap the definition of 'handleNext' in its own useCallback() Hook.",
    
  5561.               output: normalizeIndent`
    
  5562.                 function MyComponent(props) {
    
  5563.                   let [, setState] = useState();
    
  5564. 
    
  5565.                   const handleNext = useCallback((value) => {
    
  5566.                     setState(value);
    
  5567.                   });
    
  5568. 
    
  5569.                   useEffect(() => {
    
  5570.                     return Store.subscribe(handleNext);
    
  5571.                   }, [handleNext]);
    
  5572. 
    
  5573.                   return <div onClick={handleNext} />;
    
  5574.                 }
    
  5575.               `,
    
  5576.             },
    
  5577.           ],
    
  5578.         },
    
  5579.       ],
    
  5580.     },
    
  5581.     {
    
  5582.       code: normalizeIndent`
    
  5583.         function MyComponent(props) {
    
  5584.           function handleNext1() {
    
  5585.             console.log('hello');
    
  5586.           }
    
  5587.           const handleNext2 = () => {
    
  5588.             console.log('hello');
    
  5589.           };
    
  5590.           let handleNext3 = function() {
    
  5591.             console.log('hello');
    
  5592.           };
    
  5593.           useEffect(() => {
    
  5594.             return Store.subscribe(handleNext1);
    
  5595.           }, [handleNext1]);
    
  5596.           useLayoutEffect(() => {
    
  5597.             return Store.subscribe(handleNext2);
    
  5598.           }, [handleNext2]);
    
  5599.           useMemo(() => {
    
  5600.             return Store.subscribe(handleNext3);
    
  5601.           }, [handleNext3]);
    
  5602.         }
    
  5603.       `,
    
  5604.       errors: [
    
  5605.         {
    
  5606.           message:
    
  5607.             "The 'handleNext1' function makes the dependencies of useEffect Hook " +
    
  5608.             '(at line 14) change on every render. Move it inside the useEffect callback. ' +
    
  5609.             "Alternatively, wrap the definition of 'handleNext1' in its own useCallback() Hook.",
    
  5610.           suggestions: undefined,
    
  5611.         },
    
  5612.         {
    
  5613.           message:
    
  5614.             "The 'handleNext2' function makes the dependencies of useLayoutEffect Hook " +
    
  5615.             '(at line 17) change on every render. Move it inside the useLayoutEffect callback. ' +
    
  5616.             "Alternatively, wrap the definition of 'handleNext2' in its own useCallback() Hook.",
    
  5617.           suggestions: undefined,
    
  5618.         },
    
  5619.         {
    
  5620.           message:
    
  5621.             "The 'handleNext3' function makes the dependencies of useMemo Hook " +
    
  5622.             '(at line 20) change on every render. Move it inside the useMemo callback. ' +
    
  5623.             "Alternatively, wrap the definition of 'handleNext3' in its own useCallback() Hook.",
    
  5624.           suggestions: undefined,
    
  5625.         },
    
  5626.       ],
    
  5627.     },
    
  5628.     {
    
  5629.       code: normalizeIndent`
    
  5630.         function MyComponent(props) {
    
  5631.           function handleNext1() {
    
  5632.             console.log('hello');
    
  5633.           }
    
  5634.           const handleNext2 = () => {
    
  5635.             console.log('hello');
    
  5636.           };
    
  5637.           let handleNext3 = function() {
    
  5638.             console.log('hello');
    
  5639.           };
    
  5640.           useEffect(() => {
    
  5641.             handleNext1();
    
  5642.             return Store.subscribe(() => handleNext1());
    
  5643.           }, [handleNext1]);
    
  5644.           useLayoutEffect(() => {
    
  5645.             handleNext2();
    
  5646.             return Store.subscribe(() => handleNext2());
    
  5647.           }, [handleNext2]);
    
  5648.           useMemo(() => {
    
  5649.             handleNext3();
    
  5650.             return Store.subscribe(() => handleNext3());
    
  5651.           }, [handleNext3]);
    
  5652.         }
    
  5653.       `,
    
  5654.       // Suggestions don't wrap into useCallback here
    
  5655.       // because they are only referenced by effect itself.
    
  5656.       errors: [
    
  5657.         {
    
  5658.           message:
    
  5659.             "The 'handleNext1' function makes the dependencies of useEffect Hook " +
    
  5660.             '(at line 15) change on every render. Move it inside the useEffect callback. ' +
    
  5661.             "Alternatively, wrap the definition of 'handleNext1' in its own useCallback() Hook.",
    
  5662.           suggestions: undefined,
    
  5663.         },
    
  5664.         {
    
  5665.           message:
    
  5666.             "The 'handleNext2' function makes the dependencies of useLayoutEffect Hook " +
    
  5667.             '(at line 19) change on every render. Move it inside the useLayoutEffect callback. ' +
    
  5668.             "Alternatively, wrap the definition of 'handleNext2' in its own useCallback() Hook.",
    
  5669.           suggestions: undefined,
    
  5670.         },
    
  5671.         {
    
  5672.           message:
    
  5673.             "The 'handleNext3' function makes the dependencies of useMemo Hook " +
    
  5674.             '(at line 23) change on every render. Move it inside the useMemo callback. ' +
    
  5675.             "Alternatively, wrap the definition of 'handleNext3' in its own useCallback() Hook.",
    
  5676.           suggestions: undefined,
    
  5677.         },
    
  5678.       ],
    
  5679.     },
    
  5680.     {
    
  5681.       code: normalizeIndent`
    
  5682.         function MyComponent(props) {
    
  5683.           function handleNext1() {
    
  5684.             console.log('hello');
    
  5685.           }
    
  5686.           const handleNext2 = () => {
    
  5687.             console.log('hello');
    
  5688.           };
    
  5689.           let handleNext3 = function() {
    
  5690.             console.log('hello');
    
  5691.           };
    
  5692.           useEffect(() => {
    
  5693.             handleNext1();
    
  5694.             return Store.subscribe(() => handleNext1());
    
  5695.           }, [handleNext1]);
    
  5696.           useLayoutEffect(() => {
    
  5697.             handleNext2();
    
  5698.             return Store.subscribe(() => handleNext2());
    
  5699.           }, [handleNext2]);
    
  5700.           useMemo(() => {
    
  5701.             handleNext3();
    
  5702.             return Store.subscribe(() => handleNext3());
    
  5703.           }, [handleNext3]);
    
  5704.           return (
    
  5705.             <div
    
  5706.               onClick={() => {
    
  5707.                 handleNext1();
    
  5708.                 setTimeout(handleNext2);
    
  5709.                 setTimeout(() => {
    
  5710.                   handleNext3();
    
  5711.                 });
    
  5712.               }}
    
  5713.             />
    
  5714.           );
    
  5715.         }
    
  5716.       `,
    
  5717.       errors: [
    
  5718.         {
    
  5719.           message:
    
  5720.             "The 'handleNext1' function makes the dependencies of useEffect Hook " +
    
  5721.             '(at line 15) change on every render. To fix this, wrap the ' +
    
  5722.             "definition of 'handleNext1' in its own useCallback() Hook.",
    
  5723.           suggestions: undefined,
    
  5724.         },
    
  5725.         {
    
  5726.           message:
    
  5727.             "The 'handleNext2' function makes the dependencies of useLayoutEffect Hook " +
    
  5728.             '(at line 19) change on every render. To fix this, wrap the ' +
    
  5729.             "definition of 'handleNext2' in its own useCallback() Hook.",
    
  5730.           // Suggestion wraps into useCallback where possible (variables only)
    
  5731.           // because they are only referenced outside the effect.
    
  5732.           suggestions: [
    
  5733.             {
    
  5734.               desc: "Wrap the definition of 'handleNext2' in its own useCallback() Hook.",
    
  5735.               output: normalizeIndent`
    
  5736.                 function MyComponent(props) {
    
  5737.                   function handleNext1() {
    
  5738.                     console.log('hello');
    
  5739.                   }
    
  5740.                   const handleNext2 = useCallback(() => {
    
  5741.                     console.log('hello');
    
  5742.                   });
    
  5743.                   let handleNext3 = function() {
    
  5744.                     console.log('hello');
    
  5745.                   };
    
  5746.                   useEffect(() => {
    
  5747.                     handleNext1();
    
  5748.                     return Store.subscribe(() => handleNext1());
    
  5749.                   }, [handleNext1]);
    
  5750.                   useLayoutEffect(() => {
    
  5751.                     handleNext2();
    
  5752.                     return Store.subscribe(() => handleNext2());
    
  5753.                   }, [handleNext2]);
    
  5754.                   useMemo(() => {
    
  5755.                     handleNext3();
    
  5756.                     return Store.subscribe(() => handleNext3());
    
  5757.                   }, [handleNext3]);
    
  5758.                   return (
    
  5759.                     <div
    
  5760.                       onClick={() => {
    
  5761.                         handleNext1();
    
  5762.                         setTimeout(handleNext2);
    
  5763.                         setTimeout(() => {
    
  5764.                           handleNext3();
    
  5765.                         });
    
  5766.                       }}
    
  5767.                     />
    
  5768.                   );
    
  5769.                 }
    
  5770.               `,
    
  5771.             },
    
  5772.           ],
    
  5773.         },
    
  5774.         {
    
  5775.           message:
    
  5776.             "The 'handleNext3' function makes the dependencies of useMemo Hook " +
    
  5777.             '(at line 23) change on every render. To fix this, wrap the ' +
    
  5778.             "definition of 'handleNext3' in its own useCallback() Hook.",
    
  5779.           // Autofix wraps into useCallback where possible (variables only)
    
  5780.           // because they are only referenced outside the effect.
    
  5781.           suggestions: [
    
  5782.             {
    
  5783.               desc: "Wrap the definition of 'handleNext3' in its own useCallback() Hook.",
    
  5784.               output: normalizeIndent`
    
  5785.                 function MyComponent(props) {
    
  5786.                   function handleNext1() {
    
  5787.                     console.log('hello');
    
  5788.                   }
    
  5789.                   const handleNext2 = () => {
    
  5790.                     console.log('hello');
    
  5791.                   };
    
  5792.                   let handleNext3 = useCallback(function() {
    
  5793.                     console.log('hello');
    
  5794.                   });
    
  5795.                   useEffect(() => {
    
  5796.                     handleNext1();
    
  5797.                     return Store.subscribe(() => handleNext1());
    
  5798.                   }, [handleNext1]);
    
  5799.                   useLayoutEffect(() => {
    
  5800.                     handleNext2();
    
  5801.                     return Store.subscribe(() => handleNext2());
    
  5802.                   }, [handleNext2]);
    
  5803.                   useMemo(() => {
    
  5804.                     handleNext3();
    
  5805.                     return Store.subscribe(() => handleNext3());
    
  5806.                   }, [handleNext3]);
    
  5807.                   return (
    
  5808.                     <div
    
  5809.                       onClick={() => {
    
  5810.                         handleNext1();
    
  5811.                         setTimeout(handleNext2);
    
  5812.                         setTimeout(() => {
    
  5813.                           handleNext3();
    
  5814.                         });
    
  5815.                       }}
    
  5816.                     />
    
  5817.                   );
    
  5818.                 }
    
  5819.               `,
    
  5820.             },
    
  5821.           ],
    
  5822.         },
    
  5823.       ],
    
  5824.     },
    
  5825.     {
    
  5826.       code: normalizeIndent`
    
  5827.         function MyComponent(props) {
    
  5828.           const handleNext1 = () => {
    
  5829.             console.log('hello');
    
  5830.           };
    
  5831.           function handleNext2() {
    
  5832.             console.log('hello');
    
  5833.           }
    
  5834.           useEffect(() => {
    
  5835.             return Store.subscribe(handleNext1);
    
  5836.             return Store.subscribe(handleNext2);
    
  5837.           }, [handleNext1, handleNext2]);
    
  5838.           useEffect(() => {
    
  5839.             return Store.subscribe(handleNext1);
    
  5840.             return Store.subscribe(handleNext2);
    
  5841.           }, [handleNext1, handleNext2]);
    
  5842.         }
    
  5843.       `,
    
  5844.       // Normally we'd suggest moving handleNext inside an
    
  5845.       // effect. But it's used by more than one. So we
    
  5846.       // suggest useCallback() and use it for the autofix
    
  5847.       // where possible (variable but not declaration).
    
  5848.       // TODO: we could coalesce messages for the same function if it affects multiple Hooks.
    
  5849.       errors: [
    
  5850.         {
    
  5851.           message:
    
  5852.             "The 'handleNext1' function makes the dependencies of useEffect Hook " +
    
  5853.             '(at line 12) change on every render. To fix this, wrap the ' +
    
  5854.             "definition of 'handleNext1' in its own useCallback() Hook.",
    
  5855.           suggestions: [
    
  5856.             {
    
  5857.               desc: "Wrap the definition of 'handleNext1' in its own useCallback() Hook.",
    
  5858.               output: normalizeIndent`
    
  5859.                 function MyComponent(props) {
    
  5860.                   const handleNext1 = useCallback(() => {
    
  5861.                     console.log('hello');
    
  5862.                   });
    
  5863.                   function handleNext2() {
    
  5864.                     console.log('hello');
    
  5865.                   }
    
  5866.                   useEffect(() => {
    
  5867.                     return Store.subscribe(handleNext1);
    
  5868.                     return Store.subscribe(handleNext2);
    
  5869.                   }, [handleNext1, handleNext2]);
    
  5870.                   useEffect(() => {
    
  5871.                     return Store.subscribe(handleNext1);
    
  5872.                     return Store.subscribe(handleNext2);
    
  5873.                   }, [handleNext1, handleNext2]);
    
  5874.                 }
    
  5875.               `,
    
  5876.             },
    
  5877.           ],
    
  5878.         },
    
  5879.         {
    
  5880.           message:
    
  5881.             "The 'handleNext1' function makes the dependencies of useEffect Hook " +
    
  5882.             '(at line 16) change on every render. To fix this, wrap the ' +
    
  5883.             "definition of 'handleNext1' in its own useCallback() Hook.",
    
  5884.           suggestions: [
    
  5885.             {
    
  5886.               desc: "Wrap the definition of 'handleNext1' in its own useCallback() Hook.",
    
  5887.               output: normalizeIndent`
    
  5888.                 function MyComponent(props) {
    
  5889.                   const handleNext1 = useCallback(() => {
    
  5890.                     console.log('hello');
    
  5891.                   });
    
  5892.                   function handleNext2() {
    
  5893.                     console.log('hello');
    
  5894.                   }
    
  5895.                   useEffect(() => {
    
  5896.                     return Store.subscribe(handleNext1);
    
  5897.                     return Store.subscribe(handleNext2);
    
  5898.                   }, [handleNext1, handleNext2]);
    
  5899.                   useEffect(() => {
    
  5900.                     return Store.subscribe(handleNext1);
    
  5901.                     return Store.subscribe(handleNext2);
    
  5902.                   }, [handleNext1, handleNext2]);
    
  5903.                 }
    
  5904.               `,
    
  5905.             },
    
  5906.           ],
    
  5907.         },
    
  5908.         {
    
  5909.           message:
    
  5910.             "The 'handleNext2' function makes the dependencies of useEffect Hook " +
    
  5911.             '(at line 12) change on every render. To fix this, wrap the ' +
    
  5912.             "definition of 'handleNext2' in its own useCallback() Hook.",
    
  5913.           suggestions: undefined,
    
  5914.         },
    
  5915.         {
    
  5916.           message:
    
  5917.             "The 'handleNext2' function makes the dependencies of useEffect Hook " +
    
  5918.             '(at line 16) change on every render. To fix this, wrap the ' +
    
  5919.             "definition of 'handleNext2' in its own useCallback() Hook.",
    
  5920.           suggestions: undefined,
    
  5921.         },
    
  5922.       ],
    
  5923.     },
    
  5924.     {
    
  5925.       code: normalizeIndent`
    
  5926.         function MyComponent(props) {
    
  5927.           let handleNext = () => {
    
  5928.             console.log('hello');
    
  5929.           };
    
  5930.           if (props.foo) {
    
  5931.             handleNext = () => {
    
  5932.               console.log('hello');
    
  5933.             };
    
  5934.           }
    
  5935.           useEffect(() => {
    
  5936.             return Store.subscribe(handleNext);
    
  5937.           }, [handleNext]);
    
  5938.         }
    
  5939.       `,
    
  5940.       errors: [
    
  5941.         {
    
  5942.           message:
    
  5943.             "The 'handleNext' function makes the dependencies of useEffect Hook " +
    
  5944.             '(at line 13) change on every render. To fix this, wrap the definition of ' +
    
  5945.             "'handleNext' in its own useCallback() Hook.",
    
  5946.           // Normally we'd suggest moving handleNext inside an
    
  5947.           // effect. But it's used more than once.
    
  5948.           // TODO: our autofix here isn't quite sufficient because
    
  5949.           // it only wraps the first definition. But seems ok.
    
  5950.           suggestions: [
    
  5951.             {
    
  5952.               desc: "Wrap the definition of 'handleNext' in its own useCallback() Hook.",
    
  5953.               output: normalizeIndent`
    
  5954.                 function MyComponent(props) {
    
  5955.                   let handleNext = useCallback(() => {
    
  5956.                     console.log('hello');
    
  5957.                   });
    
  5958.                   if (props.foo) {
    
  5959.                     handleNext = () => {
    
  5960.                       console.log('hello');
    
  5961.                     };
    
  5962.                   }
    
  5963.                   useEffect(() => {
    
  5964.                     return Store.subscribe(handleNext);
    
  5965.                   }, [handleNext]);
    
  5966.                 }
    
  5967.               `,
    
  5968.             },
    
  5969.           ],
    
  5970.         },
    
  5971.       ],
    
  5972.     },
    
  5973.     {
    
  5974.       code: normalizeIndent`
    
  5975.         function MyComponent(props) {
    
  5976.           let [, setState] = useState();
    
  5977.           let taint = props.foo;
    
  5978. 
    
  5979.           function handleNext(value) {
    
  5980.             let value2 = value * taint;
    
  5981.             setState(value2);
    
  5982.             console.log('hello');
    
  5983.           }
    
  5984. 
    
  5985.           useEffect(() => {
    
  5986.             return Store.subscribe(handleNext);
    
  5987.           }, [handleNext]);
    
  5988.         }
    
  5989.       `,
    
  5990.       errors: [
    
  5991.         {
    
  5992.           message:
    
  5993.             `The 'handleNext' function makes the dependencies of ` +
    
  5994.             `useEffect Hook (at line 14) change on every render. ` +
    
  5995.             `Move it inside the useEffect callback. Alternatively, wrap the ` +
    
  5996.             `definition of 'handleNext' in its own useCallback() Hook.`,
    
  5997.           suggestions: undefined,
    
  5998.         },
    
  5999.       ],
    
  6000.     },
    
  6001.     {
    
  6002.       code: normalizeIndent`
    
  6003.         function Counter() {
    
  6004.           let [count, setCount] = useState(0);
    
  6005. 
    
  6006.           useEffect(() => {
    
  6007.             let id = setInterval(() => {
    
  6008.               setCount(count + 1);
    
  6009.             }, 1000);
    
  6010.             return () => clearInterval(id);
    
  6011.           }, []);
    
  6012. 
    
  6013.           return <h1>{count}</h1>;
    
  6014.         }
    
  6015.       `,
    
  6016.       errors: [
    
  6017.         {
    
  6018.           message:
    
  6019.             "React Hook useEffect has a missing dependency: 'count'. " +
    
  6020.             'Either include it or remove the dependency array. ' +
    
  6021.             `You can also do a functional update 'setCount(c => ...)' if you ` +
    
  6022.             `only need 'count' in the 'setCount' call.`,
    
  6023.           suggestions: [
    
  6024.             {
    
  6025.               desc: 'Update the dependencies array to be: [count]',
    
  6026.               output: normalizeIndent`
    
  6027.                 function Counter() {
    
  6028.                   let [count, setCount] = useState(0);
    
  6029. 
    
  6030.                   useEffect(() => {
    
  6031.                     let id = setInterval(() => {
    
  6032.                       setCount(count + 1);
    
  6033.                     }, 1000);
    
  6034.                     return () => clearInterval(id);
    
  6035.                   }, [count]);
    
  6036. 
    
  6037.                   return <h1>{count}</h1>;
    
  6038.                 }
    
  6039.               `,
    
  6040.             },
    
  6041.           ],
    
  6042.         },
    
  6043.       ],
    
  6044.     },
    
  6045.     {
    
  6046.       code: normalizeIndent`
    
  6047.         function Counter() {
    
  6048.           let [count, setCount] = useState(0);
    
  6049.           let [increment, setIncrement] = useState(0);
    
  6050. 
    
  6051.           useEffect(() => {
    
  6052.             let id = setInterval(() => {
    
  6053.               setCount(count + increment);
    
  6054.             }, 1000);
    
  6055.             return () => clearInterval(id);
    
  6056.           }, []);
    
  6057. 
    
  6058.           return <h1>{count}</h1>;
    
  6059.         }
    
  6060.       `,
    
  6061.       errors: [
    
  6062.         {
    
  6063.           message:
    
  6064.             "React Hook useEffect has missing dependencies: 'count' and 'increment'. " +
    
  6065.             'Either include them or remove the dependency array. ' +
    
  6066.             `You can also do a functional update 'setCount(c => ...)' if you ` +
    
  6067.             `only need 'count' in the 'setCount' call.`,
    
  6068.           suggestions: [
    
  6069.             {
    
  6070.               desc: 'Update the dependencies array to be: [count, increment]',
    
  6071.               output: normalizeIndent`
    
  6072.                 function Counter() {
    
  6073.                   let [count, setCount] = useState(0);
    
  6074.                   let [increment, setIncrement] = useState(0);
    
  6075. 
    
  6076.                   useEffect(() => {
    
  6077.                     let id = setInterval(() => {
    
  6078.                       setCount(count + increment);
    
  6079.                     }, 1000);
    
  6080.                     return () => clearInterval(id);
    
  6081.                   }, [count, increment]);
    
  6082. 
    
  6083.                   return <h1>{count}</h1>;
    
  6084.                 }
    
  6085.               `,
    
  6086.             },
    
  6087.           ],
    
  6088.         },
    
  6089.       ],
    
  6090.     },
    
  6091.     {
    
  6092.       code: normalizeIndent`
    
  6093.         function Counter() {
    
  6094.           let [count, setCount] = useState(0);
    
  6095.           let [increment, setIncrement] = useState(0);
    
  6096. 
    
  6097.           useEffect(() => {
    
  6098.             let id = setInterval(() => {
    
  6099.               setCount(count => count + increment);
    
  6100.             }, 1000);
    
  6101.             return () => clearInterval(id);
    
  6102.           }, []);
    
  6103. 
    
  6104.           return <h1>{count}</h1>;
    
  6105.         }
    
  6106.       `,
    
  6107.       errors: [
    
  6108.         {
    
  6109.           message:
    
  6110.             "React Hook useEffect has a missing dependency: 'increment'. " +
    
  6111.             'Either include it or remove the dependency array. ' +
    
  6112.             `You can also replace multiple useState variables with useReducer ` +
    
  6113.             `if 'setCount' needs the current value of 'increment'.`,
    
  6114.           suggestions: [
    
  6115.             {
    
  6116.               desc: 'Update the dependencies array to be: [increment]',
    
  6117.               output: normalizeIndent`
    
  6118.                 function Counter() {
    
  6119.                   let [count, setCount] = useState(0);
    
  6120.                   let [increment, setIncrement] = useState(0);
    
  6121. 
    
  6122.                   useEffect(() => {
    
  6123.                     let id = setInterval(() => {
    
  6124.                       setCount(count => count + increment);
    
  6125.                     }, 1000);
    
  6126.                     return () => clearInterval(id);
    
  6127.                   }, [increment]);
    
  6128. 
    
  6129.                   return <h1>{count}</h1>;
    
  6130.                 }
    
  6131.               `,
    
  6132.             },
    
  6133.           ],
    
  6134.         },
    
  6135.       ],
    
  6136.     },
    
  6137.     {
    
  6138.       code: normalizeIndent`
    
  6139.         function Counter() {
    
  6140.           let [count, setCount] = useState(0);
    
  6141.           let increment = useCustomHook();
    
  6142. 
    
  6143.           useEffect(() => {
    
  6144.             let id = setInterval(() => {
    
  6145.               setCount(count => count + increment);
    
  6146.             }, 1000);
    
  6147.             return () => clearInterval(id);
    
  6148.           }, []);
    
  6149. 
    
  6150.           return <h1>{count}</h1>;
    
  6151.         }
    
  6152.       `,
    
  6153.       // This intentionally doesn't show the reducer message
    
  6154.       // because we don't know if it's safe for it to close over a value.
    
  6155.       // We only show it for state variables (and possibly props).
    
  6156.       errors: [
    
  6157.         {
    
  6158.           message:
    
  6159.             "React Hook useEffect has a missing dependency: 'increment'. " +
    
  6160.             'Either include it or remove the dependency array.',
    
  6161.           suggestions: [
    
  6162.             {
    
  6163.               desc: 'Update the dependencies array to be: [increment]',
    
  6164.               output: normalizeIndent`
    
  6165.                 function Counter() {
    
  6166.                   let [count, setCount] = useState(0);
    
  6167.                   let increment = useCustomHook();
    
  6168. 
    
  6169.                   useEffect(() => {
    
  6170.                     let id = setInterval(() => {
    
  6171.                       setCount(count => count + increment);
    
  6172.                     }, 1000);
    
  6173.                     return () => clearInterval(id);
    
  6174.                   }, [increment]);
    
  6175. 
    
  6176.                   return <h1>{count}</h1>;
    
  6177.                 }
    
  6178.               `,
    
  6179.             },
    
  6180.           ],
    
  6181.         },
    
  6182.       ],
    
  6183.     },
    
  6184.     {
    
  6185.       code: normalizeIndent`
    
  6186.         function Counter({ step }) {
    
  6187.           let [count, setCount] = useState(0);
    
  6188. 
    
  6189.           function increment(x) {
    
  6190.             return x + step;
    
  6191.           }
    
  6192. 
    
  6193.           useEffect(() => {
    
  6194.             let id = setInterval(() => {
    
  6195.               setCount(count => increment(count));
    
  6196.             }, 1000);
    
  6197.             return () => clearInterval(id);
    
  6198.           }, []);
    
  6199. 
    
  6200.           return <h1>{count}</h1>;
    
  6201.         }
    
  6202.       `,
    
  6203.       // This intentionally doesn't show the reducer message
    
  6204.       // because we don't know if it's safe for it to close over a value.
    
  6205.       // We only show it for state variables (and possibly props).
    
  6206.       errors: [
    
  6207.         {
    
  6208.           message:
    
  6209.             "React Hook useEffect has a missing dependency: 'increment'. " +
    
  6210.             'Either include it or remove the dependency array.',
    
  6211.           suggestions: [
    
  6212.             {
    
  6213.               desc: 'Update the dependencies array to be: [increment]',
    
  6214.               output: normalizeIndent`
    
  6215.                 function Counter({ step }) {
    
  6216.                   let [count, setCount] = useState(0);
    
  6217. 
    
  6218.                   function increment(x) {
    
  6219.                     return x + step;
    
  6220.                   }
    
  6221. 
    
  6222.                   useEffect(() => {
    
  6223.                     let id = setInterval(() => {
    
  6224.                       setCount(count => increment(count));
    
  6225.                     }, 1000);
    
  6226.                     return () => clearInterval(id);
    
  6227.                   }, [increment]);
    
  6228. 
    
  6229.                   return <h1>{count}</h1>;
    
  6230.                 }
    
  6231.               `,
    
  6232.             },
    
  6233.           ],
    
  6234.         },
    
  6235.       ],
    
  6236.     },
    
  6237.     {
    
  6238.       code: normalizeIndent`
    
  6239.         function Counter({ step }) {
    
  6240.           let [count, setCount] = useState(0);
    
  6241. 
    
  6242.           function increment(x) {
    
  6243.             return x + step;
    
  6244.           }
    
  6245. 
    
  6246.           useEffect(() => {
    
  6247.             let id = setInterval(() => {
    
  6248.               setCount(count => increment(count));
    
  6249.             }, 1000);
    
  6250.             return () => clearInterval(id);
    
  6251.           }, [increment]);
    
  6252. 
    
  6253.           return <h1>{count}</h1>;
    
  6254.         }
    
  6255.       `,
    
  6256.       errors: [
    
  6257.         {
    
  6258.           message:
    
  6259.             `The 'increment' function makes the dependencies of useEffect Hook ` +
    
  6260.             `(at line 14) change on every render. Move it inside the useEffect callback. ` +
    
  6261.             `Alternatively, wrap the definition of \'increment\' in its own ` +
    
  6262.             `useCallback() Hook.`,
    
  6263.           suggestions: undefined,
    
  6264.         },
    
  6265.       ],
    
  6266.     },
    
  6267.     {
    
  6268.       code: normalizeIndent`
    
  6269.         function Counter({ increment }) {
    
  6270.           let [count, setCount] = useState(0);
    
  6271. 
    
  6272.           useEffect(() => {
    
  6273.             let id = setInterval(() => {
    
  6274.               setCount(count => count + increment);
    
  6275.             }, 1000);
    
  6276.             return () => clearInterval(id);
    
  6277.           }, []);
    
  6278. 
    
  6279.           return <h1>{count}</h1>;
    
  6280.         }
    
  6281.       `,
    
  6282.       errors: [
    
  6283.         {
    
  6284.           message:
    
  6285.             "React Hook useEffect has a missing dependency: 'increment'. " +
    
  6286.             'Either include it or remove the dependency array. ' +
    
  6287.             `If 'setCount' needs the current value of 'increment', ` +
    
  6288.             `you can also switch to useReducer instead of useState and read 'increment' in the reducer.`,
    
  6289.           suggestions: [
    
  6290.             {
    
  6291.               desc: 'Update the dependencies array to be: [increment]',
    
  6292.               output: normalizeIndent`
    
  6293.                 function Counter({ increment }) {
    
  6294.                   let [count, setCount] = useState(0);
    
  6295. 
    
  6296.                   useEffect(() => {
    
  6297.                     let id = setInterval(() => {
    
  6298.                       setCount(count => count + increment);
    
  6299.                     }, 1000);
    
  6300.                     return () => clearInterval(id);
    
  6301.                   }, [increment]);
    
  6302. 
    
  6303.                   return <h1>{count}</h1>;
    
  6304.                 }
    
  6305.               `,
    
  6306.             },
    
  6307.           ],
    
  6308.         },
    
  6309.       ],
    
  6310.     },
    
  6311.     {
    
  6312.       code: normalizeIndent`
    
  6313.         function Counter() {
    
  6314.           const [count, setCount] = useState(0);
    
  6315. 
    
  6316.           function tick() {
    
  6317.             setCount(count + 1);
    
  6318.           }
    
  6319. 
    
  6320.           useEffect(() => {
    
  6321.             let id = setInterval(() => {
    
  6322.               tick();
    
  6323.             }, 1000);
    
  6324.             return () => clearInterval(id);
    
  6325.           }, []);
    
  6326. 
    
  6327.           return <h1>{count}</h1>;
    
  6328.         }
    
  6329.       `,
    
  6330.       // TODO: ideally this should suggest useState updater form
    
  6331.       // since this code doesn't actually work. The autofix could
    
  6332.       // at least avoid suggesting 'tick' since it's obviously
    
  6333.       // always different, and thus useless.
    
  6334.       errors: [
    
  6335.         {
    
  6336.           message:
    
  6337.             "React Hook useEffect has a missing dependency: 'tick'. " +
    
  6338.             'Either include it or remove the dependency array.',
    
  6339.           suggestions: [
    
  6340.             {
    
  6341.               desc: 'Update the dependencies array to be: [tick]',
    
  6342.               output: normalizeIndent`
    
  6343.                 function Counter() {
    
  6344.                   const [count, setCount] = useState(0);
    
  6345. 
    
  6346.                   function tick() {
    
  6347.                     setCount(count + 1);
    
  6348.                   }
    
  6349. 
    
  6350.                   useEffect(() => {
    
  6351.                     let id = setInterval(() => {
    
  6352.                       tick();
    
  6353.                     }, 1000);
    
  6354.                     return () => clearInterval(id);
    
  6355.                   }, [tick]);
    
  6356. 
    
  6357.                   return <h1>{count}</h1>;
    
  6358.                 }
    
  6359.               `,
    
  6360.             },
    
  6361.           ],
    
  6362.         },
    
  6363.       ],
    
  6364.     },
    
  6365.     {
    
  6366.       // Regression test for a crash
    
  6367.       code: normalizeIndent`
    
  6368.         function Podcasts() {
    
  6369.           useEffect(() => {
    
  6370.             alert(podcasts);
    
  6371.           }, []);
    
  6372.           let [podcasts, setPodcasts] = useState(null);
    
  6373.         }
    
  6374.       `,
    
  6375.       errors: [
    
  6376.         {
    
  6377.           message:
    
  6378.             `React Hook useEffect has a missing dependency: 'podcasts'. ` +
    
  6379.             `Either include it or remove the dependency array.`,
    
  6380.           // Note: this autofix is shady because
    
  6381.           // the variable is used before declaration.
    
  6382.           // TODO: Maybe we can catch those fixes and not autofix.
    
  6383.           suggestions: [
    
  6384.             {
    
  6385.               desc: 'Update the dependencies array to be: [podcasts]',
    
  6386.               output: normalizeIndent`
    
  6387.                 function Podcasts() {
    
  6388.                   useEffect(() => {
    
  6389.                     alert(podcasts);
    
  6390.                   }, [podcasts]);
    
  6391.                   let [podcasts, setPodcasts] = useState(null);
    
  6392.                 }
    
  6393.               `,
    
  6394.             },
    
  6395.           ],
    
  6396.         },
    
  6397.       ],
    
  6398.     },
    
  6399.     {
    
  6400.       code: normalizeIndent`
    
  6401.         function Podcasts({ fetchPodcasts, id }) {
    
  6402.           let [podcasts, setPodcasts] = useState(null);
    
  6403.           useEffect(() => {
    
  6404.             fetchPodcasts(id).then(setPodcasts);
    
  6405.           }, [id]);
    
  6406.         }
    
  6407.       `,
    
  6408.       errors: [
    
  6409.         {
    
  6410.           message:
    
  6411.             `React Hook useEffect has a missing dependency: 'fetchPodcasts'. ` +
    
  6412.             `Either include it or remove the dependency array. ` +
    
  6413.             `If 'fetchPodcasts' changes too often, ` +
    
  6414.             `find the parent component that defines it and wrap that definition in useCallback.`,
    
  6415.           suggestions: [
    
  6416.             {
    
  6417.               desc: 'Update the dependencies array to be: [fetchPodcasts, id]',
    
  6418.               output: normalizeIndent`
    
  6419.                 function Podcasts({ fetchPodcasts, id }) {
    
  6420.                   let [podcasts, setPodcasts] = useState(null);
    
  6421.                   useEffect(() => {
    
  6422.                     fetchPodcasts(id).then(setPodcasts);
    
  6423.                   }, [fetchPodcasts, id]);
    
  6424.                 }
    
  6425.               `,
    
  6426.             },
    
  6427.           ],
    
  6428.         },
    
  6429.       ],
    
  6430.     },
    
  6431.     {
    
  6432.       code: normalizeIndent`
    
  6433.         function Podcasts({ api: { fetchPodcasts }, id }) {
    
  6434.           let [podcasts, setPodcasts] = useState(null);
    
  6435.           useEffect(() => {
    
  6436.             fetchPodcasts(id).then(setPodcasts);
    
  6437.           }, [id]);
    
  6438.         }
    
  6439.       `,
    
  6440.       errors: [
    
  6441.         {
    
  6442.           message:
    
  6443.             `React Hook useEffect has a missing dependency: 'fetchPodcasts'. ` +
    
  6444.             `Either include it or remove the dependency array. ` +
    
  6445.             `If 'fetchPodcasts' changes too often, ` +
    
  6446.             `find the parent component that defines it and wrap that definition in useCallback.`,
    
  6447.           suggestions: [
    
  6448.             {
    
  6449.               desc: 'Update the dependencies array to be: [fetchPodcasts, id]',
    
  6450.               output: normalizeIndent`
    
  6451.                 function Podcasts({ api: { fetchPodcasts }, id }) {
    
  6452.                   let [podcasts, setPodcasts] = useState(null);
    
  6453.                   useEffect(() => {
    
  6454.                     fetchPodcasts(id).then(setPodcasts);
    
  6455.                   }, [fetchPodcasts, id]);
    
  6456.                 }
    
  6457.               `,
    
  6458.             },
    
  6459.           ],
    
  6460.         },
    
  6461.       ],
    
  6462.     },
    
  6463.     {
    
  6464.       code: normalizeIndent`
    
  6465.         function Podcasts({ fetchPodcasts, fetchPodcasts2, id }) {
    
  6466.           let [podcasts, setPodcasts] = useState(null);
    
  6467.           useEffect(() => {
    
  6468.             setTimeout(() => {
    
  6469.               console.log(id);
    
  6470.               fetchPodcasts(id).then(setPodcasts);
    
  6471.               fetchPodcasts2(id).then(setPodcasts);
    
  6472.             });
    
  6473.           }, [id]);
    
  6474.         }
    
  6475.       `,
    
  6476.       errors: [
    
  6477.         {
    
  6478.           message:
    
  6479.             `React Hook useEffect has missing dependencies: 'fetchPodcasts' and 'fetchPodcasts2'. ` +
    
  6480.             `Either include them or remove the dependency array. ` +
    
  6481.             `If 'fetchPodcasts' changes too often, ` +
    
  6482.             `find the parent component that defines it and wrap that definition in useCallback.`,
    
  6483.           suggestions: [
    
  6484.             {
    
  6485.               desc: 'Update the dependencies array to be: [fetchPodcasts, fetchPodcasts2, id]',
    
  6486.               output: normalizeIndent`
    
  6487.                 function Podcasts({ fetchPodcasts, fetchPodcasts2, id }) {
    
  6488.                   let [podcasts, setPodcasts] = useState(null);
    
  6489.                   useEffect(() => {
    
  6490.                     setTimeout(() => {
    
  6491.                       console.log(id);
    
  6492.                       fetchPodcasts(id).then(setPodcasts);
    
  6493.                       fetchPodcasts2(id).then(setPodcasts);
    
  6494.                     });
    
  6495.                   }, [fetchPodcasts, fetchPodcasts2, id]);
    
  6496.                 }
    
  6497.               `,
    
  6498.             },
    
  6499.           ],
    
  6500.         },
    
  6501.       ],
    
  6502.     },
    
  6503.     {
    
  6504.       code: normalizeIndent`
    
  6505.         function Podcasts({ fetchPodcasts, id }) {
    
  6506.           let [podcasts, setPodcasts] = useState(null);
    
  6507.           useEffect(() => {
    
  6508.             console.log(fetchPodcasts);
    
  6509.             fetchPodcasts(id).then(setPodcasts);
    
  6510.           }, [id]);
    
  6511.         }
    
  6512.       `,
    
  6513.       errors: [
    
  6514.         {
    
  6515.           message:
    
  6516.             `React Hook useEffect has a missing dependency: 'fetchPodcasts'. ` +
    
  6517.             `Either include it or remove the dependency array. ` +
    
  6518.             `If 'fetchPodcasts' changes too often, ` +
    
  6519.             `find the parent component that defines it and wrap that definition in useCallback.`,
    
  6520.           suggestions: [
    
  6521.             {
    
  6522.               desc: 'Update the dependencies array to be: [fetchPodcasts, id]',
    
  6523.               output: normalizeIndent`
    
  6524.                 function Podcasts({ fetchPodcasts, id }) {
    
  6525.                   let [podcasts, setPodcasts] = useState(null);
    
  6526.                   useEffect(() => {
    
  6527.                     console.log(fetchPodcasts);
    
  6528.                     fetchPodcasts(id).then(setPodcasts);
    
  6529.                   }, [fetchPodcasts, id]);
    
  6530.                 }
    
  6531.               `,
    
  6532.             },
    
  6533.           ],
    
  6534.         },
    
  6535.       ],
    
  6536.     },
    
  6537.     {
    
  6538.       code: normalizeIndent`
    
  6539.         function Podcasts({ fetchPodcasts, id }) {
    
  6540.           let [podcasts, setPodcasts] = useState(null);
    
  6541.           useEffect(() => {
    
  6542.             console.log(fetchPodcasts);
    
  6543.             fetchPodcasts?.(id).then(setPodcasts);
    
  6544.           }, [id]);
    
  6545.         }
    
  6546.       `,
    
  6547.       errors: [
    
  6548.         {
    
  6549.           message:
    
  6550.             `React Hook useEffect has a missing dependency: 'fetchPodcasts'. ` +
    
  6551.             `Either include it or remove the dependency array. ` +
    
  6552.             `If 'fetchPodcasts' changes too often, ` +
    
  6553.             `find the parent component that defines it and wrap that definition in useCallback.`,
    
  6554.           suggestions: [
    
  6555.             {
    
  6556.               desc: 'Update the dependencies array to be: [fetchPodcasts, id]',
    
  6557.               output: normalizeIndent`
    
  6558.                 function Podcasts({ fetchPodcasts, id }) {
    
  6559.                   let [podcasts, setPodcasts] = useState(null);
    
  6560.                   useEffect(() => {
    
  6561.                     console.log(fetchPodcasts);
    
  6562.                     fetchPodcasts?.(id).then(setPodcasts);
    
  6563.                   }, [fetchPodcasts, id]);
    
  6564.                 }
    
  6565.               `,
    
  6566.             },
    
  6567.           ],
    
  6568.         },
    
  6569.       ],
    
  6570.     },
    
  6571.     {
    
  6572.       // The mistake here is that it was moved inside the effect
    
  6573.       // so it can't be referenced in the deps array.
    
  6574.       code: normalizeIndent`
    
  6575.         function Thing() {
    
  6576.           useEffect(() => {
    
  6577.             const fetchData = async () => {};
    
  6578.             fetchData();
    
  6579.           }, [fetchData]);
    
  6580.         }
    
  6581.       `,
    
  6582.       errors: [
    
  6583.         {
    
  6584.           message:
    
  6585.             `React Hook useEffect has an unnecessary dependency: 'fetchData'. ` +
    
  6586.             `Either exclude it or remove the dependency array.`,
    
  6587.           suggestions: [
    
  6588.             {
    
  6589.               desc: 'Update the dependencies array to be: []',
    
  6590.               output: normalizeIndent`
    
  6591.                 function Thing() {
    
  6592.                   useEffect(() => {
    
  6593.                     const fetchData = async () => {};
    
  6594.                     fetchData();
    
  6595.                   }, []);
    
  6596.                 }
    
  6597.               `,
    
  6598.             },
    
  6599.           ],
    
  6600.         },
    
  6601.       ],
    
  6602.     },
    
  6603.     {
    
  6604.       code: normalizeIndent`
    
  6605.         function Hello() {
    
  6606.           const [state, setState] = useState(0);
    
  6607.           useEffect(() => {
    
  6608.             setState({});
    
  6609.           });
    
  6610.         }
    
  6611.       `,
    
  6612.       errors: [
    
  6613.         {
    
  6614.           message:
    
  6615.             `React Hook useEffect contains a call to 'setState'. ` +
    
  6616.             `Without a list of dependencies, this can lead to an infinite chain of updates. ` +
    
  6617.             `To fix this, pass [] as a second argument to the useEffect Hook.`,
    
  6618.           suggestions: [
    
  6619.             {
    
  6620.               desc: 'Add dependencies array: []',
    
  6621.               output: normalizeIndent`
    
  6622.                 function Hello() {
    
  6623.                   const [state, setState] = useState(0);
    
  6624.                   useEffect(() => {
    
  6625.                     setState({});
    
  6626.                   }, []);
    
  6627.                 }
    
  6628.               `,
    
  6629.             },
    
  6630.           ],
    
  6631.         },
    
  6632.       ],
    
  6633.     },
    
  6634.     {
    
  6635.       code: normalizeIndent`
    
  6636.         function Hello() {
    
  6637.           const [data, setData] = useState(0);
    
  6638.           useEffect(() => {
    
  6639.             fetchData.then(setData);
    
  6640.           });
    
  6641.         }
    
  6642.       `,
    
  6643.       errors: [
    
  6644.         {
    
  6645.           message:
    
  6646.             `React Hook useEffect contains a call to 'setData'. ` +
    
  6647.             `Without a list of dependencies, this can lead to an infinite chain of updates. ` +
    
  6648.             `To fix this, pass [] as a second argument to the useEffect Hook.`,
    
  6649.           suggestions: [
    
  6650.             {
    
  6651.               desc: 'Add dependencies array: []',
    
  6652.               output: normalizeIndent`
    
  6653.                 function Hello() {
    
  6654.                   const [data, setData] = useState(0);
    
  6655.                   useEffect(() => {
    
  6656.                     fetchData.then(setData);
    
  6657.                   }, []);
    
  6658.                 }
    
  6659.               `,
    
  6660.             },
    
  6661.           ],
    
  6662.         },
    
  6663.       ],
    
  6664.     },
    
  6665.     {
    
  6666.       code: normalizeIndent`
    
  6667.         function Hello({ country }) {
    
  6668.           const [data, setData] = useState(0);
    
  6669.           useEffect(() => {
    
  6670.             fetchData(country).then(setData);
    
  6671.           });
    
  6672.         }
    
  6673.       `,
    
  6674.       errors: [
    
  6675.         {
    
  6676.           message:
    
  6677.             `React Hook useEffect contains a call to 'setData'. ` +
    
  6678.             `Without a list of dependencies, this can lead to an infinite chain of updates. ` +
    
  6679.             `To fix this, pass [country] as a second argument to the useEffect Hook.`,
    
  6680.           suggestions: [
    
  6681.             {
    
  6682.               desc: 'Add dependencies array: [country]',
    
  6683.               output: normalizeIndent`
    
  6684.                 function Hello({ country }) {
    
  6685.                   const [data, setData] = useState(0);
    
  6686.                   useEffect(() => {
    
  6687.                     fetchData(country).then(setData);
    
  6688.                   }, [country]);
    
  6689.                 }
    
  6690.               `,
    
  6691.             },
    
  6692.           ],
    
  6693.         },
    
  6694.       ],
    
  6695.     },
    
  6696.     {
    
  6697.       code: normalizeIndent`
    
  6698.         function Hello({ prop1, prop2 }) {
    
  6699.           const [state, setState] = useState(0);
    
  6700.           useEffect(() => {
    
  6701.             if (prop1) {
    
  6702.               setState(prop2);
    
  6703.             }
    
  6704.           });
    
  6705.         }
    
  6706.       `,
    
  6707.       errors: [
    
  6708.         {
    
  6709.           message:
    
  6710.             `React Hook useEffect contains a call to 'setState'. ` +
    
  6711.             `Without a list of dependencies, this can lead to an infinite chain of updates. ` +
    
  6712.             `To fix this, pass [prop1, prop2] as a second argument to the useEffect Hook.`,
    
  6713.           suggestions: [
    
  6714.             {
    
  6715.               desc: 'Add dependencies array: [prop1, prop2]',
    
  6716.               output: normalizeIndent`
    
  6717.                 function Hello({ prop1, prop2 }) {
    
  6718.                   const [state, setState] = useState(0);
    
  6719.                   useEffect(() => {
    
  6720.                     if (prop1) {
    
  6721.                       setState(prop2);
    
  6722.                     }
    
  6723.                   }, [prop1, prop2]);
    
  6724.                 }
    
  6725.               `,
    
  6726.             },
    
  6727.           ],
    
  6728.         },
    
  6729.       ],
    
  6730.     },
    
  6731.     {
    
  6732.       code: normalizeIndent`
    
  6733.         function Thing() {
    
  6734.           useEffect(async () => {}, []);
    
  6735.         }
    
  6736.       `,
    
  6737.       errors: [
    
  6738.         {
    
  6739.           message:
    
  6740.             `Effect callbacks are synchronous to prevent race conditions. ` +
    
  6741.             `Put the async function inside:\n\n` +
    
  6742.             'useEffect(() => {\n' +
    
  6743.             '  async function fetchData() {\n' +
    
  6744.             '    // You can await here\n' +
    
  6745.             '    const response = await MyAPI.getData(someId);\n' +
    
  6746.             '    // ...\n' +
    
  6747.             '  }\n' +
    
  6748.             '  fetchData();\n' +
    
  6749.             `}, [someId]); // Or [] if effect doesn't need props or state\n\n` +
    
  6750.             'Learn more about data fetching with Hooks: https://reactjs.org/link/hooks-data-fetching',
    
  6751.           suggestions: undefined,
    
  6752.         },
    
  6753.       ],
    
  6754.     },
    
  6755.     {
    
  6756.       code: normalizeIndent`
    
  6757.         function Thing() {
    
  6758.           useEffect(async () => {});
    
  6759.         }
    
  6760.       `,
    
  6761.       errors: [
    
  6762.         {
    
  6763.           message:
    
  6764.             `Effect callbacks are synchronous to prevent race conditions. ` +
    
  6765.             `Put the async function inside:\n\n` +
    
  6766.             'useEffect(() => {\n' +
    
  6767.             '  async function fetchData() {\n' +
    
  6768.             '    // You can await here\n' +
    
  6769.             '    const response = await MyAPI.getData(someId);\n' +
    
  6770.             '    // ...\n' +
    
  6771.             '  }\n' +
    
  6772.             '  fetchData();\n' +
    
  6773.             `}, [someId]); // Or [] if effect doesn't need props or state\n\n` +
    
  6774.             'Learn more about data fetching with Hooks: https://reactjs.org/link/hooks-data-fetching',
    
  6775.           suggestions: undefined,
    
  6776.         },
    
  6777.       ],
    
  6778.     },
    
  6779.     {
    
  6780.       code: normalizeIndent`
    
  6781.         function Example() {
    
  6782.           const foo = useCallback(() => {
    
  6783.             foo();
    
  6784.           }, [foo]);
    
  6785.         }
    
  6786.       `,
    
  6787.       errors: [
    
  6788.         {
    
  6789.           message:
    
  6790.             "React Hook useCallback has an unnecessary dependency: 'foo'. " +
    
  6791.             'Either exclude it or remove the dependency array.',
    
  6792.           suggestions: [
    
  6793.             {
    
  6794.               desc: 'Update the dependencies array to be: []',
    
  6795.               output: normalizeIndent`
    
  6796.                 function Example() {
    
  6797.                   const foo = useCallback(() => {
    
  6798.                     foo();
    
  6799.                   }, []);
    
  6800.                 }
    
  6801.               `,
    
  6802.             },
    
  6803.           ],
    
  6804.         },
    
  6805.       ],
    
  6806.     },
    
  6807.     {
    
  6808.       code: normalizeIndent`
    
  6809.         function Example({ prop }) {
    
  6810.           const foo = useCallback(() => {
    
  6811.             prop.hello(foo);
    
  6812.           }, [foo]);
    
  6813.           const bar = useCallback(() => {
    
  6814.             foo();
    
  6815.           }, [foo]);
    
  6816.         }
    
  6817.       `,
    
  6818.       errors: [
    
  6819.         {
    
  6820.           message:
    
  6821.             "React Hook useCallback has a missing dependency: 'prop'. " +
    
  6822.             'Either include it or remove the dependency array.',
    
  6823.           suggestions: [
    
  6824.             {
    
  6825.               desc: 'Update the dependencies array to be: [prop]',
    
  6826.               output: normalizeIndent`
    
  6827.                 function Example({ prop }) {
    
  6828.                   const foo = useCallback(() => {
    
  6829.                     prop.hello(foo);
    
  6830.                   }, [prop]);
    
  6831.                   const bar = useCallback(() => {
    
  6832.                     foo();
    
  6833.                   }, [foo]);
    
  6834.                 }
    
  6835.               `,
    
  6836.             },
    
  6837.           ],
    
  6838.         },
    
  6839.       ],
    
  6840.     },
    
  6841.     {
    
  6842.       code: normalizeIndent`
    
  6843.         function MyComponent() {
    
  6844.           const local = {};
    
  6845.           function myEffect() {
    
  6846.             console.log(local);
    
  6847.           }
    
  6848.           useEffect(myEffect, []);
    
  6849.         }
    
  6850.       `,
    
  6851.       errors: [
    
  6852.         {
    
  6853.           message:
    
  6854.             "React Hook useEffect has a missing dependency: 'local'. " +
    
  6855.             'Either include it or remove the dependency array.',
    
  6856.           suggestions: [
    
  6857.             {
    
  6858.               desc: 'Update the dependencies array to be: [local]',
    
  6859.               output: normalizeIndent`
    
  6860.                 function MyComponent() {
    
  6861.                   const local = {};
    
  6862.                   function myEffect() {
    
  6863.                     console.log(local);
    
  6864.                   }
    
  6865.                   useEffect(myEffect, [local]);
    
  6866.                 }
    
  6867.               `,
    
  6868.             },
    
  6869.           ],
    
  6870.         },
    
  6871.       ],
    
  6872.     },
    
  6873.     {
    
  6874.       code: normalizeIndent`
    
  6875.         function MyComponent() {
    
  6876.           const local = {};
    
  6877.           const myEffect = () => {
    
  6878.             console.log(local);
    
  6879.           };
    
  6880.           useEffect(myEffect, []);
    
  6881.         }
    
  6882.       `,
    
  6883.       errors: [
    
  6884.         {
    
  6885.           message:
    
  6886.             "React Hook useEffect has a missing dependency: 'local'. " +
    
  6887.             'Either include it or remove the dependency array.',
    
  6888.           suggestions: [
    
  6889.             {
    
  6890.               desc: 'Update the dependencies array to be: [local]',
    
  6891.               output: normalizeIndent`
    
  6892.                 function MyComponent() {
    
  6893.                   const local = {};
    
  6894.                   const myEffect = () => {
    
  6895.                     console.log(local);
    
  6896.                   };
    
  6897.                   useEffect(myEffect, [local]);
    
  6898.                 }
    
  6899.               `,
    
  6900.             },
    
  6901.           ],
    
  6902.         },
    
  6903.       ],
    
  6904.     },
    
  6905.     {
    
  6906.       code: normalizeIndent`
    
  6907.         function MyComponent() {
    
  6908.           const local = {};
    
  6909.           const myEffect = function() {
    
  6910.             console.log(local);
    
  6911.           };
    
  6912.           useEffect(myEffect, []);
    
  6913.         }
    
  6914.       `,
    
  6915.       errors: [
    
  6916.         {
    
  6917.           message:
    
  6918.             "React Hook useEffect has a missing dependency: 'local'. " +
    
  6919.             'Either include it or remove the dependency array.',
    
  6920.           suggestions: [
    
  6921.             {
    
  6922.               desc: 'Update the dependencies array to be: [local]',
    
  6923.               output: normalizeIndent`
    
  6924.                 function MyComponent() {
    
  6925.                   const local = {};
    
  6926.                   const myEffect = function() {
    
  6927.                     console.log(local);
    
  6928.                   };
    
  6929.                   useEffect(myEffect, [local]);
    
  6930.                 }
    
  6931.               `,
    
  6932.             },
    
  6933.           ],
    
  6934.         },
    
  6935.       ],
    
  6936.     },
    
  6937.     {
    
  6938.       code: normalizeIndent`
    
  6939.         function MyComponent() {
    
  6940.           const local = {};
    
  6941.           const myEffect = () => {
    
  6942.             otherThing();
    
  6943.           };
    
  6944.           const otherThing = () => {
    
  6945.             console.log(local);
    
  6946.           };
    
  6947.           useEffect(myEffect, []);
    
  6948.         }
    
  6949.       `,
    
  6950.       errors: [
    
  6951.         {
    
  6952.           message:
    
  6953.             "React Hook useEffect has a missing dependency: 'otherThing'. " +
    
  6954.             'Either include it or remove the dependency array.',
    
  6955.           suggestions: [
    
  6956.             {
    
  6957.               desc: 'Update the dependencies array to be: [otherThing]',
    
  6958.               output: normalizeIndent`
    
  6959.                 function MyComponent() {
    
  6960.                   const local = {};
    
  6961.                   const myEffect = () => {
    
  6962.                     otherThing();
    
  6963.                   };
    
  6964.                   const otherThing = () => {
    
  6965.                     console.log(local);
    
  6966.                   };
    
  6967.                   useEffect(myEffect, [otherThing]);
    
  6968.                 }
    
  6969.               `,
    
  6970.             },
    
  6971.           ],
    
  6972.         },
    
  6973.       ],
    
  6974.     },
    
  6975.     {
    
  6976.       code: normalizeIndent`
    
  6977.         function MyComponent() {
    
  6978.           const local = {};
    
  6979.           const myEffect = debounce(() => {
    
  6980.             console.log(local);
    
  6981.           }, delay);
    
  6982.           useEffect(myEffect, []);
    
  6983.         }
    
  6984.       `,
    
  6985.       errors: [
    
  6986.         {
    
  6987.           message:
    
  6988.             "React Hook useEffect has a missing dependency: 'myEffect'. " +
    
  6989.             'Either include it or remove the dependency array.',
    
  6990.           suggestions: [
    
  6991.             {
    
  6992.               desc: 'Update the dependencies array to be: [myEffect]',
    
  6993.               output: normalizeIndent`
    
  6994.                 function MyComponent() {
    
  6995.                   const local = {};
    
  6996.                   const myEffect = debounce(() => {
    
  6997.                     console.log(local);
    
  6998.                   }, delay);
    
  6999.                   useEffect(myEffect, [myEffect]);
    
  7000.                 }
    
  7001.               `,
    
  7002.             },
    
  7003.           ],
    
  7004.         },
    
  7005.       ],
    
  7006.     },
    
  7007.     {
    
  7008.       code: normalizeIndent`
    
  7009.         function MyComponent() {
    
  7010.           const local = {};
    
  7011.           const myEffect = debounce(() => {
    
  7012.             console.log(local);
    
  7013.           }, delay);
    
  7014.           useEffect(myEffect, [local]);
    
  7015.         }
    
  7016.       `,
    
  7017.       errors: [
    
  7018.         {
    
  7019.           message:
    
  7020.             "React Hook useEffect has a missing dependency: 'myEffect'. " +
    
  7021.             'Either include it or remove the dependency array.',
    
  7022.           suggestions: [
    
  7023.             {
    
  7024.               desc: 'Update the dependencies array to be: [myEffect]',
    
  7025.               output: normalizeIndent`
    
  7026.                 function MyComponent() {
    
  7027.                   const local = {};
    
  7028.                   const myEffect = debounce(() => {
    
  7029.                     console.log(local);
    
  7030.                   }, delay);
    
  7031.                   useEffect(myEffect, [myEffect]);
    
  7032.                 }
    
  7033.               `,
    
  7034.             },
    
  7035.           ],
    
  7036.         },
    
  7037.       ],
    
  7038.     },
    
  7039.     {
    
  7040.       code: normalizeIndent`
    
  7041.         function MyComponent({myEffect}) {
    
  7042.           useEffect(myEffect, []);
    
  7043.         }
    
  7044.       `,
    
  7045.       errors: [
    
  7046.         {
    
  7047.           message:
    
  7048.             "React Hook useEffect has a missing dependency: 'myEffect'. " +
    
  7049.             'Either include it or remove the dependency array.',
    
  7050.           suggestions: [
    
  7051.             {
    
  7052.               desc: 'Update the dependencies array to be: [myEffect]',
    
  7053.               output: normalizeIndent`
    
  7054.                 function MyComponent({myEffect}) {
    
  7055.                   useEffect(myEffect, [myEffect]);
    
  7056.                 }
    
  7057.               `,
    
  7058.             },
    
  7059.           ],
    
  7060.         },
    
  7061.       ],
    
  7062.     },
    
  7063.     {
    
  7064.       code: normalizeIndent`
    
  7065.         function MyComponent() {
    
  7066.           const local = {};
    
  7067.           useEffect(debounce(() => {
    
  7068.             console.log(local);
    
  7069.           }, delay), []);
    
  7070.         }
    
  7071.       `,
    
  7072.       errors: [
    
  7073.         {
    
  7074.           message:
    
  7075.             'React Hook useEffect received a function whose dependencies ' +
    
  7076.             'are unknown. Pass an inline function instead.',
    
  7077.           suggestions: [],
    
  7078.         },
    
  7079.       ],
    
  7080.     },
    
  7081.     {
    
  7082.       code: normalizeIndent`
    
  7083.         function MyComponent() {
    
  7084.           const local = {};
    
  7085.           useEffect(() => {
    
  7086.             console.log(local);
    
  7087.           }, []);
    
  7088.         }
    
  7089.       `,
    
  7090.       // Dangerous autofix is enabled due to the option:
    
  7091.       output: normalizeIndent`
    
  7092.         function MyComponent() {
    
  7093.           const local = {};
    
  7094.           useEffect(() => {
    
  7095.             console.log(local);
    
  7096.           }, [local]);
    
  7097.         }
    
  7098.       `,
    
  7099.       errors: [
    
  7100.         {
    
  7101.           message:
    
  7102.             "React Hook useEffect has a missing dependency: 'local'. " +
    
  7103.             'Either include it or remove the dependency array.',
    
  7104.         },
    
  7105.       ],
    
  7106.       // Keep this until major IDEs and VS Code FB ESLint plugin support Suggestions API.
    
  7107.       options: [{enableDangerousAutofixThisMayCauseInfiniteLoops: true}],
    
  7108.     },
    
  7109.     {
    
  7110.       code: normalizeIndent`
    
  7111.         function MyComponent(props) {
    
  7112.           let foo = {}
    
  7113.           useEffect(() => {
    
  7114.             foo.bar.baz = 43;
    
  7115.             props.foo.bar.baz = 1;
    
  7116.           }, []);
    
  7117.         }
    
  7118.       `,
    
  7119.       errors: [
    
  7120.         {
    
  7121.           message:
    
  7122.             "React Hook useEffect has missing dependencies: 'foo.bar' and 'props.foo.bar'. " +
    
  7123.             'Either include them or remove the dependency array.',
    
  7124.           suggestions: [
    
  7125.             {
    
  7126.               desc: 'Update the dependencies array to be: [foo.bar, props.foo.bar]',
    
  7127.               output: normalizeIndent`
    
  7128.                 function MyComponent(props) {
    
  7129.                   let foo = {}
    
  7130.                   useEffect(() => {
    
  7131.                     foo.bar.baz = 43;
    
  7132.                     props.foo.bar.baz = 1;
    
  7133.                   }, [foo.bar, props.foo.bar]);
    
  7134.                 }
    
  7135.               `,
    
  7136.             },
    
  7137.           ],
    
  7138.         },
    
  7139.       ],
    
  7140.     },
    
  7141.     {
    
  7142.       code: normalizeIndent`
    
  7143.         function Component() {
    
  7144.           const foo = {};
    
  7145.           useMemo(() => foo, [foo]);
    
  7146.         }
    
  7147.       `,
    
  7148.       errors: [
    
  7149.         {
    
  7150.           message:
    
  7151.             "The 'foo' object makes the dependencies of useMemo Hook (at line 4) change on every render. " +
    
  7152.             "Move it inside the useMemo callback. Alternatively, wrap the initialization of 'foo' in its own " +
    
  7153.             'useMemo() Hook.',
    
  7154.           suggestions: undefined,
    
  7155.         },
    
  7156.       ],
    
  7157.     },
    
  7158.     {
    
  7159.       code: normalizeIndent`
    
  7160.         function Component() {
    
  7161.           const foo = [];
    
  7162.           useMemo(() => foo, [foo]);
    
  7163.         }
    
  7164.       `,
    
  7165.       errors: [
    
  7166.         {
    
  7167.           message:
    
  7168.             "The 'foo' array makes the dependencies of useMemo Hook (at line 4) change on every render. " +
    
  7169.             "Move it inside the useMemo callback. Alternatively, wrap the initialization of 'foo' in its own " +
    
  7170.             'useMemo() Hook.',
    
  7171.           suggestions: undefined,
    
  7172.         },
    
  7173.       ],
    
  7174.     },
    
  7175.     {
    
  7176.       code: normalizeIndent`
    
  7177.         function Component() {
    
  7178.           const foo = () => {};
    
  7179.           useMemo(() => foo, [foo]);
    
  7180.         }
    
  7181.       `,
    
  7182.       errors: [
    
  7183.         {
    
  7184.           message:
    
  7185.             "The 'foo' function makes the dependencies of useMemo Hook (at line 4) change on every render. " +
    
  7186.             "Move it inside the useMemo callback. Alternatively, wrap the definition of 'foo' in its own " +
    
  7187.             'useCallback() Hook.',
    
  7188.           suggestions: undefined,
    
  7189.         },
    
  7190.       ],
    
  7191.     },
    
  7192.     {
    
  7193.       code: normalizeIndent`
    
  7194.         function Component() {
    
  7195.           const foo = function bar(){};
    
  7196.           useMemo(() => foo, [foo]);
    
  7197.         }
    
  7198.       `,
    
  7199.       errors: [
    
  7200.         {
    
  7201.           message:
    
  7202.             "The 'foo' function makes the dependencies of useMemo Hook (at line 4) change on every render. " +
    
  7203.             "Move it inside the useMemo callback. Alternatively, wrap the definition of 'foo' in its own " +
    
  7204.             'useCallback() Hook.',
    
  7205.           suggestions: undefined,
    
  7206.         },
    
  7207.       ],
    
  7208.     },
    
  7209.     {
    
  7210.       code: normalizeIndent`
    
  7211.         function Component() {
    
  7212.           const foo = class {};
    
  7213.           useMemo(() => foo, [foo]);
    
  7214.         }
    
  7215.       `,
    
  7216.       errors: [
    
  7217.         {
    
  7218.           message:
    
  7219.             "The 'foo' class makes the dependencies of useMemo Hook (at line 4) change on every render. " +
    
  7220.             "Move it inside the useMemo callback. Alternatively, wrap the initialization of 'foo' in its own " +
    
  7221.             'useMemo() Hook.',
    
  7222.           suggestions: undefined,
    
  7223.         },
    
  7224.       ],
    
  7225.     },
    
  7226.     {
    
  7227.       code: normalizeIndent`
    
  7228.         function Component() {
    
  7229.           const foo = true ? {} : "fine";
    
  7230.           useMemo(() => foo, [foo]);
    
  7231.         }
    
  7232.       `,
    
  7233.       errors: [
    
  7234.         {
    
  7235.           message:
    
  7236.             "The 'foo' conditional could make the dependencies of useMemo Hook (at line 4) change on every render. " +
    
  7237.             "Move it inside the useMemo callback. Alternatively, wrap the initialization of 'foo' in its own " +
    
  7238.             'useMemo() Hook.',
    
  7239.           suggestions: undefined,
    
  7240.         },
    
  7241.       ],
    
  7242.     },
    
  7243.     {
    
  7244.       code: normalizeIndent`
    
  7245.         function Component() {
    
  7246.           const foo = bar || {};
    
  7247.           useMemo(() => foo, [foo]);
    
  7248.         }
    
  7249.       `,
    
  7250.       errors: [
    
  7251.         {
    
  7252.           message:
    
  7253.             "The 'foo' logical expression could make the dependencies of useMemo Hook (at line 4) change on every render. " +
    
  7254.             "Move it inside the useMemo callback. Alternatively, wrap the initialization of 'foo' in its own " +
    
  7255.             'useMemo() Hook.',
    
  7256.           suggestions: undefined,
    
  7257.         },
    
  7258.       ],
    
  7259.     },
    
  7260.     {
    
  7261.       code: normalizeIndent`
    
  7262.         function Component() {
    
  7263.           const foo = bar ?? {};
    
  7264.           useMemo(() => foo, [foo]);
    
  7265.         }
    
  7266.       `,
    
  7267.       errors: [
    
  7268.         {
    
  7269.           message:
    
  7270.             "The 'foo' logical expression could make the dependencies of useMemo Hook (at line 4) change on every render. " +
    
  7271.             "Move it inside the useMemo callback. Alternatively, wrap the initialization of 'foo' in its own " +
    
  7272.             'useMemo() Hook.',
    
  7273.           suggestions: undefined,
    
  7274.         },
    
  7275.       ],
    
  7276.     },
    
  7277.     {
    
  7278.       code: normalizeIndent`
    
  7279.         function Component() {
    
  7280.           const foo = bar && {};
    
  7281.           useMemo(() => foo, [foo]);
    
  7282.         }
    
  7283.       `,
    
  7284.       errors: [
    
  7285.         {
    
  7286.           message:
    
  7287.             "The 'foo' logical expression could make the dependencies of useMemo Hook (at line 4) change on every render. " +
    
  7288.             "Move it inside the useMemo callback. Alternatively, wrap the initialization of 'foo' in its own " +
    
  7289.             'useMemo() Hook.',
    
  7290.           suggestions: undefined,
    
  7291.         },
    
  7292.       ],
    
  7293.     },
    
  7294.     {
    
  7295.       code: normalizeIndent`
    
  7296.         function Component() {
    
  7297.           const foo = bar ? baz ? {} : null : null;
    
  7298.           useMemo(() => foo, [foo]);
    
  7299.         }
    
  7300.       `,
    
  7301.       errors: [
    
  7302.         {
    
  7303.           message:
    
  7304.             "The 'foo' conditional could make the dependencies of useMemo Hook (at line 4) change on every render. " +
    
  7305.             "Move it inside the useMemo callback. Alternatively, wrap the initialization of 'foo' in its own " +
    
  7306.             'useMemo() Hook.',
    
  7307.           suggestions: undefined,
    
  7308.         },
    
  7309.       ],
    
  7310.     },
    
  7311.     {
    
  7312.       code: normalizeIndent`
    
  7313.         function Component() {
    
  7314.           let foo = {};
    
  7315.           useMemo(() => foo, [foo]);
    
  7316.         }
    
  7317.       `,
    
  7318.       errors: [
    
  7319.         {
    
  7320.           message:
    
  7321.             "The 'foo' object makes the dependencies of useMemo Hook (at line 4) change on every render. " +
    
  7322.             "Move it inside the useMemo callback. Alternatively, wrap the initialization of 'foo' in its own " +
    
  7323.             'useMemo() Hook.',
    
  7324.           suggestions: undefined,
    
  7325.         },
    
  7326.       ],
    
  7327.     },
    
  7328.     {
    
  7329.       code: normalizeIndent`
    
  7330.         function Component() {
    
  7331.           var foo = {};
    
  7332.           useMemo(() => foo, [foo]);
    
  7333.         }
    
  7334.       `,
    
  7335.       errors: [
    
  7336.         {
    
  7337.           message:
    
  7338.             "The 'foo' object makes the dependencies of useMemo Hook (at line 4) change on every render. " +
    
  7339.             "Move it inside the useMemo callback. Alternatively, wrap the initialization of 'foo' in its own " +
    
  7340.             'useMemo() Hook.',
    
  7341.           suggestions: undefined,
    
  7342.         },
    
  7343.       ],
    
  7344.     },
    
  7345.     {
    
  7346.       code: normalizeIndent`
    
  7347.         function Component() {
    
  7348.           const foo = {};
    
  7349.           useCallback(() => {
    
  7350.             console.log(foo);
    
  7351.           }, [foo]);
    
  7352.         }
    
  7353.       `,
    
  7354.       errors: [
    
  7355.         {
    
  7356.           message:
    
  7357.             "The 'foo' object makes the dependencies of useCallback Hook (at line 6) change on every render. " +
    
  7358.             "Move it inside the useCallback callback. Alternatively, wrap the initialization of 'foo' in its own " +
    
  7359.             'useMemo() Hook.',
    
  7360.           suggestions: undefined,
    
  7361.         },
    
  7362.       ],
    
  7363.     },
    
  7364.     {
    
  7365.       code: normalizeIndent`
    
  7366.         function Component() {
    
  7367.           const foo = {};
    
  7368.           useEffect(() => {
    
  7369.             console.log(foo);
    
  7370.           }, [foo]);
    
  7371.         }
    
  7372.       `,
    
  7373.       errors: [
    
  7374.         {
    
  7375.           message:
    
  7376.             "The 'foo' object makes the dependencies of useEffect Hook (at line 6) change on every render. " +
    
  7377.             "Move it inside the useEffect callback. Alternatively, wrap the initialization of 'foo' in its own " +
    
  7378.             'useMemo() Hook.',
    
  7379.           suggestions: undefined,
    
  7380.         },
    
  7381.       ],
    
  7382.     },
    
  7383.     {
    
  7384.       code: normalizeIndent`
    
  7385.         function Component() {
    
  7386.           const foo = {};
    
  7387.           useLayoutEffect(() => {
    
  7388.             console.log(foo);
    
  7389.           }, [foo]);
    
  7390.         }
    
  7391.       `,
    
  7392.       errors: [
    
  7393.         {
    
  7394.           message:
    
  7395.             "The 'foo' object makes the dependencies of useLayoutEffect Hook (at line 6) change on every render. " +
    
  7396.             "Move it inside the useLayoutEffect callback. Alternatively, wrap the initialization of 'foo' in its own " +
    
  7397.             'useMemo() Hook.',
    
  7398.           suggestions: undefined,
    
  7399.         },
    
  7400.       ],
    
  7401.     },
    
  7402.     {
    
  7403.       code: normalizeIndent`
    
  7404.         function Component() {
    
  7405.           const foo = {};
    
  7406.           useImperativeHandle(
    
  7407.             ref,
    
  7408.             () => {
    
  7409.                console.log(foo);
    
  7410.             },
    
  7411.             [foo]
    
  7412.           );
    
  7413.         }
    
  7414.       `,
    
  7415.       errors: [
    
  7416.         {
    
  7417.           message:
    
  7418.             "The 'foo' object makes the dependencies of useImperativeHandle Hook (at line 9) change on every render. " +
    
  7419.             "Move it inside the useImperativeHandle callback. Alternatively, wrap the initialization of 'foo' in its own " +
    
  7420.             'useMemo() Hook.',
    
  7421.           suggestions: undefined,
    
  7422.         },
    
  7423.       ],
    
  7424.     },
    
  7425.     {
    
  7426.       code: normalizeIndent`
    
  7427.         function Foo(section) {
    
  7428.           const foo = section.section_components?.edges ?? [];
    
  7429.           useEffect(() => {
    
  7430.             console.log(foo);
    
  7431.           }, [foo]);
    
  7432.         }
    
  7433.       `,
    
  7434.       errors: [
    
  7435.         {
    
  7436.           message:
    
  7437.             "The 'foo' logical expression could make the dependencies of useEffect Hook (at line 6) change on every render. " +
    
  7438.             "Move it inside the useEffect callback. Alternatively, wrap the initialization of 'foo' in its own " +
    
  7439.             'useMemo() Hook.',
    
  7440.           suggestions: undefined,
    
  7441.         },
    
  7442.       ],
    
  7443.     },
    
  7444.     {
    
  7445.       code: normalizeIndent`
    
  7446.         function Foo(section) {
    
  7447.           const foo = {};
    
  7448.           console.log(foo);
    
  7449.           useMemo(() => {
    
  7450.             console.log(foo);
    
  7451.           }, [foo]);
    
  7452.         }
    
  7453.       `,
    
  7454.       errors: [
    
  7455.         {
    
  7456.           message:
    
  7457.             "The 'foo' object makes the dependencies of useMemo Hook (at line 7) change on every render. " +
    
  7458.             "To fix this, wrap the initialization of 'foo' in its own useMemo() Hook.",
    
  7459.           suggestions: undefined,
    
  7460.         },
    
  7461.       ],
    
  7462.     },
    
  7463.     {
    
  7464.       code: normalizeIndent`
    
  7465.         function Foo() {
    
  7466.           const foo = <>Hi!</>;
    
  7467.           useMemo(() => {
    
  7468.             console.log(foo);
    
  7469.           }, [foo]);
    
  7470.         }
    
  7471.       `,
    
  7472.       errors: [
    
  7473.         {
    
  7474.           message:
    
  7475.             "The 'foo' JSX fragment makes the dependencies of useMemo Hook (at line 6) change on every render. " +
    
  7476.             "Move it inside the useMemo callback. Alternatively, wrap the initialization of 'foo' in its own useMemo() Hook.",
    
  7477.           suggestions: undefined,
    
  7478.         },
    
  7479.       ],
    
  7480.     },
    
  7481.     {
    
  7482.       code: normalizeIndent`
    
  7483.         function Foo() {
    
  7484.           const foo = <div>Hi!</div>;
    
  7485.           useMemo(() => {
    
  7486.             console.log(foo);
    
  7487.           }, [foo]);
    
  7488.         }
    
  7489.       `,
    
  7490.       errors: [
    
  7491.         {
    
  7492.           message:
    
  7493.             "The 'foo' JSX element makes the dependencies of useMemo Hook (at line 6) change on every render. " +
    
  7494.             "Move it inside the useMemo callback. Alternatively, wrap the initialization of 'foo' in its own useMemo() Hook.",
    
  7495.           suggestions: undefined,
    
  7496.         },
    
  7497.       ],
    
  7498.     },
    
  7499.     {
    
  7500.       code: normalizeIndent`
    
  7501.         function Foo() {
    
  7502.           const foo = bar = {};
    
  7503.           useMemo(() => {
    
  7504.             console.log(foo);
    
  7505.           }, [foo]);
    
  7506.         }
    
  7507.       `,
    
  7508.       errors: [
    
  7509.         {
    
  7510.           message:
    
  7511.             "The 'foo' assignment expression makes the dependencies of useMemo Hook (at line 6) change on every render. " +
    
  7512.             "Move it inside the useMemo callback. Alternatively, wrap the initialization of 'foo' in its own useMemo() Hook.",
    
  7513.           suggestions: undefined,
    
  7514.         },
    
  7515.       ],
    
  7516.     },
    
  7517.     {
    
  7518.       code: normalizeIndent`
    
  7519.         function Foo() {
    
  7520.           const foo = new String('foo'); // Note 'foo' will be boxed, and thus an object and thus compared by reference.
    
  7521.           useMemo(() => {
    
  7522.             console.log(foo);
    
  7523.           }, [foo]);
    
  7524.         }
    
  7525.       `,
    
  7526.       errors: [
    
  7527.         {
    
  7528.           message:
    
  7529.             "The 'foo' object construction makes the dependencies of useMemo Hook (at line 6) change on every render. " +
    
  7530.             "Move it inside the useMemo callback. Alternatively, wrap the initialization of 'foo' in its own useMemo() Hook.",
    
  7531.           suggestions: undefined,
    
  7532.         },
    
  7533.       ],
    
  7534.     },
    
  7535.     {
    
  7536.       code: normalizeIndent`
    
  7537.         function Foo() {
    
  7538.           const foo = new Map([]);
    
  7539.           useMemo(() => {
    
  7540.             console.log(foo);
    
  7541.           }, [foo]);
    
  7542.         }
    
  7543.       `,
    
  7544.       errors: [
    
  7545.         {
    
  7546.           message:
    
  7547.             "The 'foo' object construction makes the dependencies of useMemo Hook (at line 6) change on every render. " +
    
  7548.             "Move it inside the useMemo callback. Alternatively, wrap the initialization of 'foo' in its own useMemo() Hook.",
    
  7549.           suggestions: undefined,
    
  7550.         },
    
  7551.       ],
    
  7552.     },
    
  7553.     {
    
  7554.       code: normalizeIndent`
    
  7555.         function Foo() {
    
  7556.           const foo = /reg/;
    
  7557.           useMemo(() => {
    
  7558.             console.log(foo);
    
  7559.           }, [foo]);
    
  7560.         }
    
  7561.       `,
    
  7562.       errors: [
    
  7563.         {
    
  7564.           message:
    
  7565.             "The 'foo' regular expression makes the dependencies of useMemo Hook (at line 6) change on every render. " +
    
  7566.             "Move it inside the useMemo callback. Alternatively, wrap the initialization of 'foo' in its own useMemo() Hook.",
    
  7567.           suggestions: undefined,
    
  7568.         },
    
  7569.       ],
    
  7570.     },
    
  7571. 
    
  7572.     {
    
  7573.       code: normalizeIndent`
    
  7574.         function Foo() {
    
  7575.           class Bar {};
    
  7576.           useMemo(() => {
    
  7577.             console.log(new Bar());
    
  7578.           }, [Bar]);
    
  7579.         }
    
  7580.       `,
    
  7581.       errors: [
    
  7582.         {
    
  7583.           message:
    
  7584.             "The 'Bar' class makes the dependencies of useMemo Hook (at line 6) change on every render. " +
    
  7585.             "Move it inside the useMemo callback. Alternatively, wrap the initialization of 'Bar' in its own useMemo() Hook.",
    
  7586.           suggestions: undefined,
    
  7587.         },
    
  7588.       ],
    
  7589.     },
    
  7590.     {
    
  7591.       code: normalizeIndent`
    
  7592.         function Foo() {
    
  7593.           const foo = {};
    
  7594.           useLayoutEffect(() => {
    
  7595.             console.log(foo);
    
  7596.           }, [foo]);
    
  7597.           useEffect(() => {
    
  7598.             console.log(foo);
    
  7599.           }, [foo]);
    
  7600.         }
    
  7601.       `,
    
  7602.       errors: [
    
  7603.         {
    
  7604.           message:
    
  7605.             "The 'foo' object makes the dependencies of useLayoutEffect Hook (at line 6) change on every render. " +
    
  7606.             "To fix this, wrap the initialization of 'foo' in its own useMemo() Hook.",
    
  7607.           suggestions: undefined,
    
  7608.         },
    
  7609.         {
    
  7610.           message:
    
  7611.             "The 'foo' object makes the dependencies of useEffect Hook (at line 9) change on every render. " +
    
  7612.             "To fix this, wrap the initialization of 'foo' in its own useMemo() Hook.",
    
  7613.           suggestions: undefined,
    
  7614.         },
    
  7615.       ],
    
  7616.     },
    
  7617.   ],
    
  7618. };
    
  7619. 
    
  7620. if (__EXPERIMENTAL__) {
    
  7621.   tests.valid = [
    
  7622.     ...tests.valid,
    
  7623.     {
    
  7624.       code: normalizeIndent`
    
  7625.         function MyComponent({ theme }) {
    
  7626.           const onStuff = useEffectEvent(() => {
    
  7627.             showNotification(theme);
    
  7628.           });
    
  7629.           useEffect(() => {
    
  7630.             onStuff();
    
  7631.           }, []);
    
  7632.         }
    
  7633.       `,
    
  7634.     },
    
  7635.   ];
    
  7636. 
    
  7637.   tests.invalid = [
    
  7638.     ...tests.invalid,
    
  7639.     {
    
  7640.       code: normalizeIndent`
    
  7641.         function MyComponent({ theme }) {
    
  7642.           const onStuff = useEffectEvent(() => {
    
  7643.             showNotification(theme);
    
  7644.           });
    
  7645.           useEffect(() => {
    
  7646.             onStuff();
    
  7647.           }, [onStuff]);
    
  7648.         }
    
  7649.       `,
    
  7650.       errors: [
    
  7651.         {
    
  7652.           message:
    
  7653.             'Functions returned from `useEffectEvent` must not be included in the dependency array. ' +
    
  7654.             'Remove `onStuff` from the list.',
    
  7655.           suggestions: [
    
  7656.             {
    
  7657.               desc: 'Remove the dependency `onStuff`',
    
  7658.               output: normalizeIndent`
    
  7659.                 function MyComponent({ theme }) {
    
  7660.                   const onStuff = useEffectEvent(() => {
    
  7661.                     showNotification(theme);
    
  7662.                   });
    
  7663.                   useEffect(() => {
    
  7664.                     onStuff();
    
  7665.                   }, []);
    
  7666.                 }
    
  7667.               `,
    
  7668.             },
    
  7669.           ],
    
  7670.         },
    
  7671.       ],
    
  7672.     },
    
  7673.   ];
    
  7674. }
    
  7675. 
    
  7676. // Tests that are only valid/invalid across parsers supporting Flow
    
  7677. const testsFlow = {
    
  7678.   valid: [
    
  7679.     // Ignore Generic Type Variables for arrow functions
    
  7680.     {
    
  7681.       code: normalizeIndent`
    
  7682.         function Example({ prop }) {
    
  7683.           const bar = useEffect(<T>(a: T): Hello => {
    
  7684.             prop();
    
  7685.           }, [prop]);
    
  7686.         }
    
  7687.       `,
    
  7688.     },
    
  7689.   ],
    
  7690.   invalid: [
    
  7691.     {
    
  7692.       code: normalizeIndent`
    
  7693.       function Foo() {
    
  7694.         const foo = ({}: any);
    
  7695.         useMemo(() => {
    
  7696.           console.log(foo);
    
  7697.         }, [foo]);
    
  7698.       }
    
  7699.     `,
    
  7700.       errors: [
    
  7701.         {
    
  7702.           message:
    
  7703.             "The 'foo' object makes the dependencies of useMemo Hook (at line 6) change on every render. " +
    
  7704.             "Move it inside the useMemo callback. Alternatively, wrap the initialization of 'foo' in its own useMemo() Hook.",
    
  7705.           suggestions: undefined,
    
  7706.         },
    
  7707.       ],
    
  7708.     },
    
  7709.   ],
    
  7710. };
    
  7711. 
    
  7712. // Tests that are only valid/invalid across parsers supporting TypeScript
    
  7713. const testsTypescript = {
    
  7714.   valid: [
    
  7715.     {
    
  7716.       // `ref` is still constant, despite the cast.
    
  7717.       code: normalizeIndent`
    
  7718.         function MyComponent() {
    
  7719.           const ref = useRef() as React.MutableRefObject<HTMLDivElement>;
    
  7720.           useEffect(() => {
    
  7721.             console.log(ref.current);
    
  7722.           }, []);
    
  7723.         }
    
  7724.       `,
    
  7725.     },
    
  7726.     {
    
  7727.       code: normalizeIndent`
    
  7728.         function MyComponent() {
    
  7729.           const [state, setState] = React.useState<number>(0);
    
  7730. 
    
  7731.           useEffect(() => {
    
  7732.             const someNumber: typeof state = 2;
    
  7733.             setState(prevState => prevState + someNumber);
    
  7734.           }, [])
    
  7735.         }
    
  7736.       `,
    
  7737.     },
    
  7738.     {
    
  7739.       code: normalizeIndent`
    
  7740.         function App() {
    
  7741.           const foo = {x: 1};
    
  7742.           React.useEffect(() => {
    
  7743.             const bar = {x: 2};
    
  7744.             const baz = bar as typeof foo;
    
  7745.             console.log(baz);
    
  7746.           }, []);
    
  7747.         }
    
  7748.       `,
    
  7749.     },
    
  7750.   ],
    
  7751.   invalid: [
    
  7752.     {
    
  7753.       // `local` is still non-constant, despite the cast.
    
  7754.       code: normalizeIndent`
    
  7755.         function MyComponent() {
    
  7756.           const local = {} as string;
    
  7757.           useEffect(() => {
    
  7758.             console.log(local);
    
  7759.           }, []);
    
  7760.         }
    
  7761.       `,
    
  7762.       errors: [
    
  7763.         {
    
  7764.           message:
    
  7765.             "React Hook useEffect has a missing dependency: 'local'. " +
    
  7766.             'Either include it or remove the dependency array.',
    
  7767.           suggestions: [
    
  7768.             {
    
  7769.               desc: 'Update the dependencies array to be: [local]',
    
  7770.               output: normalizeIndent`
    
  7771.                 function MyComponent() {
    
  7772.                   const local = {} as string;
    
  7773.                   useEffect(() => {
    
  7774.                     console.log(local);
    
  7775.                   }, [local]);
    
  7776.                 }
    
  7777.               `,
    
  7778.             },
    
  7779.           ],
    
  7780.         },
    
  7781.       ],
    
  7782.     },
    
  7783.     {
    
  7784.       code: normalizeIndent`
    
  7785.         function App() {
    
  7786.           const foo = {x: 1};
    
  7787.           const bar = {x: 2};
    
  7788.           useEffect(() => {
    
  7789.             const baz = bar as typeof foo;
    
  7790.             console.log(baz);
    
  7791.           }, []);
    
  7792.         }
    
  7793.       `,
    
  7794.       errors: [
    
  7795.         {
    
  7796.           message:
    
  7797.             "React Hook useEffect has a missing dependency: 'bar'. " +
    
  7798.             'Either include it or remove the dependency array.',
    
  7799.           suggestions: [
    
  7800.             {
    
  7801.               desc: 'Update the dependencies array to be: [bar]',
    
  7802.               output: normalizeIndent`
    
  7803.                 function App() {
    
  7804.                   const foo = {x: 1};
    
  7805.                   const bar = {x: 2};
    
  7806.                   useEffect(() => {
    
  7807.                     const baz = bar as typeof foo;
    
  7808.                     console.log(baz);
    
  7809.                   }, [bar]);
    
  7810.                 }
    
  7811.               `,
    
  7812.             },
    
  7813.           ],
    
  7814.         },
    
  7815.       ],
    
  7816.     },
    
  7817.     {
    
  7818.       code: normalizeIndent`
    
  7819.         function MyComponent() {
    
  7820.           const pizza = {};
    
  7821. 
    
  7822.           useEffect(() => ({
    
  7823.             crust: pizza.crust,
    
  7824.             toppings: pizza?.toppings,
    
  7825.           }), []);
    
  7826.         }
    
  7827.       `,
    
  7828.       errors: [
    
  7829.         {
    
  7830.           message:
    
  7831.             "React Hook useEffect has missing dependencies: 'pizza.crust' and 'pizza?.toppings'. " +
    
  7832.             'Either include them or remove the dependency array.',
    
  7833.           suggestions: [
    
  7834.             {
    
  7835.               desc: 'Update the dependencies array to be: [pizza.crust, pizza?.toppings]',
    
  7836.               output: normalizeIndent`
    
  7837.                 function MyComponent() {
    
  7838.                   const pizza = {};
    
  7839. 
    
  7840.                   useEffect(() => ({
    
  7841.                     crust: pizza.crust,
    
  7842.                     toppings: pizza?.toppings,
    
  7843.                   }), [pizza.crust, pizza?.toppings]);
    
  7844.                 }
    
  7845.               `,
    
  7846.             },
    
  7847.           ],
    
  7848.         },
    
  7849.       ],
    
  7850.     },
    
  7851.     {
    
  7852.       code: normalizeIndent`
    
  7853.         function MyComponent() {
    
  7854.           const pizza = {};
    
  7855. 
    
  7856.           useEffect(() => ({
    
  7857.             crust: pizza?.crust,
    
  7858.             density: pizza.crust.density,
    
  7859.           }), []);
    
  7860.         }
    
  7861.       `,
    
  7862.       errors: [
    
  7863.         {
    
  7864.           message:
    
  7865.             "React Hook useEffect has a missing dependency: 'pizza.crust'. " +
    
  7866.             'Either include it or remove the dependency array.',
    
  7867.           suggestions: [
    
  7868.             {
    
  7869.               desc: 'Update the dependencies array to be: [pizza.crust]',
    
  7870.               output: normalizeIndent`
    
  7871.                 function MyComponent() {
    
  7872.                   const pizza = {};
    
  7873. 
    
  7874.                   useEffect(() => ({
    
  7875.                     crust: pizza?.crust,
    
  7876.                     density: pizza.crust.density,
    
  7877.                   }), [pizza.crust]);
    
  7878.                 }
    
  7879.               `,
    
  7880.             },
    
  7881.           ],
    
  7882.         },
    
  7883.       ],
    
  7884.     },
    
  7885.     {
    
  7886.       code: normalizeIndent`
    
  7887.         function MyComponent() {
    
  7888.           const pizza = {};
    
  7889. 
    
  7890.           useEffect(() => ({
    
  7891.             crust: pizza.crust,
    
  7892.             density: pizza?.crust.density,
    
  7893.           }), []);
    
  7894.         }
    
  7895.       `,
    
  7896.       errors: [
    
  7897.         {
    
  7898.           message:
    
  7899.             "React Hook useEffect has a missing dependency: 'pizza.crust'. " +
    
  7900.             'Either include it or remove the dependency array.',
    
  7901.           suggestions: [
    
  7902.             {
    
  7903.               desc: 'Update the dependencies array to be: [pizza.crust]',
    
  7904.               output: normalizeIndent`
    
  7905.                 function MyComponent() {
    
  7906.                   const pizza = {};
    
  7907. 
    
  7908.                   useEffect(() => ({
    
  7909.                     crust: pizza.crust,
    
  7910.                     density: pizza?.crust.density,
    
  7911.                   }), [pizza.crust]);
    
  7912.                 }
    
  7913.               `,
    
  7914.             },
    
  7915.           ],
    
  7916.         },
    
  7917.       ],
    
  7918.     },
    
  7919.     {
    
  7920.       code: normalizeIndent`
    
  7921.         function MyComponent() {
    
  7922.           const pizza = {};
    
  7923. 
    
  7924.           useEffect(() => ({
    
  7925.             crust: pizza?.crust,
    
  7926.             density: pizza?.crust.density,
    
  7927.           }), []);
    
  7928.         }
    
  7929.       `,
    
  7930.       errors: [
    
  7931.         {
    
  7932.           message:
    
  7933.             "React Hook useEffect has a missing dependency: 'pizza?.crust'. " +
    
  7934.             'Either include it or remove the dependency array.',
    
  7935.           suggestions: [
    
  7936.             {
    
  7937.               desc: 'Update the dependencies array to be: [pizza?.crust]',
    
  7938.               output: normalizeIndent`
    
  7939.                 function MyComponent() {
    
  7940.                   const pizza = {};
    
  7941. 
    
  7942.                   useEffect(() => ({
    
  7943.                     crust: pizza?.crust,
    
  7944.                     density: pizza?.crust.density,
    
  7945.                   }), [pizza?.crust]);
    
  7946.                 }
    
  7947.               `,
    
  7948.             },
    
  7949.           ],
    
  7950.         },
    
  7951.       ],
    
  7952.     },
    
  7953.     // Regression test.
    
  7954.     {
    
  7955.       code: normalizeIndent`
    
  7956.         function Example(props) {
    
  7957.           useEffect(() => {
    
  7958.             let topHeight = 0;
    
  7959.             topHeight = props.upperViewHeight;
    
  7960.           }, []);
    
  7961.         }
    
  7962.       `,
    
  7963.       errors: [
    
  7964.         {
    
  7965.           message:
    
  7966.             "React Hook useEffect has a missing dependency: 'props.upperViewHeight'. " +
    
  7967.             'Either include it or remove the dependency array.',
    
  7968.           suggestions: [
    
  7969.             {
    
  7970.               desc: 'Update the dependencies array to be: [props.upperViewHeight]',
    
  7971.               output: normalizeIndent`
    
  7972.                 function Example(props) {
    
  7973.                   useEffect(() => {
    
  7974.                     let topHeight = 0;
    
  7975.                     topHeight = props.upperViewHeight;
    
  7976.                   }, [props.upperViewHeight]);
    
  7977.                 }
    
  7978.               `,
    
  7979.             },
    
  7980.           ],
    
  7981.         },
    
  7982.       ],
    
  7983.     },
    
  7984.     // Regression test.
    
  7985.     {
    
  7986.       code: normalizeIndent`
    
  7987.         function Example(props) {
    
  7988.           useEffect(() => {
    
  7989.             let topHeight = 0;
    
  7990.             topHeight = props?.upperViewHeight;
    
  7991.           }, []);
    
  7992.         }
    
  7993.       `,
    
  7994.       errors: [
    
  7995.         {
    
  7996.           message:
    
  7997.             "React Hook useEffect has a missing dependency: 'props?.upperViewHeight'. " +
    
  7998.             'Either include it or remove the dependency array.',
    
  7999.           suggestions: [
    
  8000.             {
    
  8001.               desc: 'Update the dependencies array to be: [props?.upperViewHeight]',
    
  8002.               output: normalizeIndent`
    
  8003.                 function Example(props) {
    
  8004.                   useEffect(() => {
    
  8005.                     let topHeight = 0;
    
  8006.                     topHeight = props?.upperViewHeight;
    
  8007.                   }, [props?.upperViewHeight]);
    
  8008.                 }
    
  8009.               `,
    
  8010.             },
    
  8011.           ],
    
  8012.         },
    
  8013.       ],
    
  8014.     },
    
  8015.     {
    
  8016.       code: normalizeIndent`
    
  8017.         function MyComponent() {
    
  8018.           const [state, setState] = React.useState<number>(0);
    
  8019. 
    
  8020.           useEffect(() => {
    
  8021.             const someNumber: typeof state = 2;
    
  8022.             setState(prevState => prevState + someNumber + state);
    
  8023.           }, [])
    
  8024.         }
    
  8025.       `,
    
  8026.       errors: [
    
  8027.         {
    
  8028.           message:
    
  8029.             "React Hook useEffect has a missing dependency: 'state'. " +
    
  8030.             'Either include it or remove the dependency array. ' +
    
  8031.             `You can also do a functional update 'setState(s => ...)' ` +
    
  8032.             `if you only need 'state' in the 'setState' call.`,
    
  8033.           suggestions: [
    
  8034.             {
    
  8035.               desc: 'Update the dependencies array to be: [state]',
    
  8036.               output: normalizeIndent`
    
  8037.               function MyComponent() {
    
  8038.                 const [state, setState] = React.useState<number>(0);
    
  8039. 
    
  8040.                 useEffect(() => {
    
  8041.                   const someNumber: typeof state = 2;
    
  8042.                   setState(prevState => prevState + someNumber + state);
    
  8043.                 }, [state])
    
  8044.               }
    
  8045.               `,
    
  8046.             },
    
  8047.           ],
    
  8048.         },
    
  8049.       ],
    
  8050.     },
    
  8051.     {
    
  8052.       code: normalizeIndent`
    
  8053.         function MyComponent() {
    
  8054.           const [state, setState] = React.useState<number>(0);
    
  8055. 
    
  8056.           useMemo(() => {
    
  8057.             const someNumber: typeof state = 2;
    
  8058.             console.log(someNumber);
    
  8059.           }, [state])
    
  8060.         }
    
  8061.       `,
    
  8062.       errors: [
    
  8063.         {
    
  8064.           message:
    
  8065.             "React Hook useMemo has an unnecessary dependency: 'state'. " +
    
  8066.             'Either exclude it or remove the dependency array.',
    
  8067.           suggestions: [
    
  8068.             {
    
  8069.               desc: 'Update the dependencies array to be: []',
    
  8070.               output: normalizeIndent`
    
  8071.                 function MyComponent() {
    
  8072.                   const [state, setState] = React.useState<number>(0);
    
  8073. 
    
  8074.                   useMemo(() => {
    
  8075.                     const someNumber: typeof state = 2;
    
  8076.                     console.log(someNumber);
    
  8077.                   }, [])
    
  8078.                 }
    
  8079.                 `,
    
  8080.             },
    
  8081.           ],
    
  8082.         },
    
  8083.       ],
    
  8084.     },
    
  8085.     {
    
  8086.       code: normalizeIndent`
    
  8087.         function Foo() {
    
  8088.           const foo = {} as any;
    
  8089.           useMemo(() => {
    
  8090.             console.log(foo);
    
  8091.           }, [foo]);
    
  8092.         }
    
  8093.       `,
    
  8094.       errors: [
    
  8095.         {
    
  8096.           message:
    
  8097.             "The 'foo' object makes the dependencies of useMemo Hook (at line 6) change on every render. " +
    
  8098.             "Move it inside the useMemo callback. Alternatively, wrap the initialization of 'foo' in its own useMemo() Hook.",
    
  8099.           suggestions: undefined,
    
  8100.         },
    
  8101.       ],
    
  8102.     },
    
  8103.   ],
    
  8104. };
    
  8105. 
    
  8106. // Tests that are only valid/invalid for `@typescript-eslint/[email protected]`
    
  8107. const testsTypescriptEslintParserV4 = {
    
  8108.   valid: [],
    
  8109.   invalid: [
    
  8110.     // TODO: Should also be invalid as part of the JS test suite i.e. be invalid with babel eslint parsers.
    
  8111.     // It doesn't use any explicit types but any JS is still valid TS.
    
  8112.     {
    
  8113.       code: normalizeIndent`
    
  8114.         function Foo({ Component }) {
    
  8115.           React.useEffect(() => {
    
  8116.             console.log(<Component />);
    
  8117.           }, []);
    
  8118.         };
    
  8119.       `,
    
  8120.       errors: [
    
  8121.         {
    
  8122.           message:
    
  8123.             "React Hook React.useEffect has a missing dependency: 'Component'. " +
    
  8124.             'Either include it or remove the dependency array.',
    
  8125.           suggestions: [
    
  8126.             {
    
  8127.               desc: 'Update the dependencies array to be: [Component]',
    
  8128.               output: normalizeIndent`
    
  8129.               function Foo({ Component }) {
    
  8130.                 React.useEffect(() => {
    
  8131.                   console.log(<Component />);
    
  8132.                 }, [Component]);
    
  8133.               };
    
  8134.             `,
    
  8135.             },
    
  8136.           ],
    
  8137.         },
    
  8138.       ],
    
  8139.     },
    
  8140.   ],
    
  8141. };
    
  8142. 
    
  8143. // For easier local testing
    
  8144. if (!process.env.CI) {
    
  8145.   let only = [];
    
  8146.   let skipped = [];
    
  8147.   [
    
  8148.     ...tests.valid,
    
  8149.     ...tests.invalid,
    
  8150.     ...testsFlow.valid,
    
  8151.     ...testsFlow.invalid,
    
  8152.     ...testsTypescript.valid,
    
  8153.     ...testsTypescript.invalid,
    
  8154.     ...testsTypescriptEslintParserV4.valid,
    
  8155.     ...testsTypescriptEslintParserV4.invalid,
    
  8156.   ].forEach(t => {
    
  8157.     if (t.skip) {
    
  8158.       delete t.skip;
    
  8159.       skipped.push(t);
    
  8160.     }
    
  8161.     if (t.only) {
    
  8162.       delete t.only;
    
  8163.       only.push(t);
    
  8164.     }
    
  8165.   });
    
  8166.   const predicate = t => {
    
  8167.     if (only.length > 0) {
    
  8168.       return only.indexOf(t) !== -1;
    
  8169.     }
    
  8170.     if (skipped.length > 0) {
    
  8171.       return skipped.indexOf(t) === -1;
    
  8172.     }
    
  8173.     return true;
    
  8174.   };
    
  8175.   tests.valid = tests.valid.filter(predicate);
    
  8176.   tests.invalid = tests.invalid.filter(predicate);
    
  8177.   testsFlow.valid = testsFlow.valid.filter(predicate);
    
  8178.   testsFlow.invalid = testsFlow.invalid.filter(predicate);
    
  8179.   testsTypescript.valid = testsTypescript.valid.filter(predicate);
    
  8180.   testsTypescript.invalid = testsTypescript.invalid.filter(predicate);
    
  8181. }
    
  8182. 
    
  8183. describe('react-hooks', () => {
    
  8184.   const parserOptions = {
    
  8185.     ecmaFeatures: {
    
  8186.       jsx: true,
    
  8187.     },
    
  8188.     ecmaVersion: 6,
    
  8189.     sourceType: 'module',
    
  8190.   };
    
  8191. 
    
  8192.   const testsBabelEslint = {
    
  8193.     valid: [...testsFlow.valid, ...tests.valid],
    
  8194.     invalid: [...testsFlow.invalid, ...tests.invalid],
    
  8195.   };
    
  8196. 
    
  8197.   new ESLintTester({
    
  8198.     parser: require.resolve('babel-eslint'),
    
  8199.     parserOptions,
    
  8200.   }).run('parser: babel-eslint', ReactHooksESLintRule, testsBabelEslint);
    
  8201. 
    
  8202.   new ESLintTester({
    
  8203.     parser: require.resolve('@babel/eslint-parser'),
    
  8204.     parserOptions,
    
  8205.   }).run(
    
  8206.     'parser: @babel/eslint-parser',
    
  8207.     ReactHooksESLintRule,
    
  8208.     testsBabelEslint
    
  8209.   );
    
  8210. 
    
  8211.   const testsTypescriptEslintParser = {
    
  8212.     valid: [...testsTypescript.valid, ...tests.valid],
    
  8213.     invalid: [...testsTypescript.invalid, ...tests.invalid],
    
  8214.   };
    
  8215. 
    
  8216.   new ESLintTester({
    
  8217.     parser: require.resolve('@typescript-eslint/parser-v2'),
    
  8218.     parserOptions,
    
  8219.   }).run(
    
  8220.     'parser: @typescript-eslint/[email protected]',
    
  8221.     ReactHooksESLintRule,
    
  8222.     testsTypescriptEslintParser
    
  8223.   );
    
  8224. 
    
  8225.   new ESLintTester({
    
  8226.     parser: require.resolve('@typescript-eslint/parser-v3'),
    
  8227.     parserOptions,
    
  8228.   }).run(
    
  8229.     'parser: @typescript-eslint/[email protected]',
    
  8230.     ReactHooksESLintRule,
    
  8231.     testsTypescriptEslintParser
    
  8232.   );
    
  8233. 
    
  8234.   new ESLintTester({
    
  8235.     parser: require.resolve('@typescript-eslint/parser-v4'),
    
  8236.     parserOptions,
    
  8237.   }).run('parser: @typescript-eslint/[email protected]', ReactHooksESLintRule, {
    
  8238.     valid: [
    
  8239.       ...testsTypescriptEslintParserV4.valid,
    
  8240.       ...testsTypescriptEslintParser.valid,
    
  8241.     ],
    
  8242.     invalid: [
    
  8243.       ...testsTypescriptEslintParserV4.invalid,
    
  8244.       ...testsTypescriptEslintParser.invalid,
    
  8245.     ],
    
  8246.   });
    
  8247. 
    
  8248.   new ESLintTester({
    
  8249.     parser: require.resolve('@typescript-eslint/parser-v5'),
    
  8250.     parserOptions,
    
  8251.   }).run('parser: @typescript-eslint/parser@^5.0.0-0', ReactHooksESLintRule, {
    
  8252.     valid: [
    
  8253.       ...testsTypescriptEslintParserV4.valid,
    
  8254.       ...testsTypescriptEslintParser.valid,
    
  8255.     ],
    
  8256.     invalid: [
    
  8257.       ...testsTypescriptEslintParserV4.invalid,
    
  8258.       ...testsTypescriptEslintParser.invalid,
    
  8259.     ],
    
  8260.   });
    
  8261. });