Best JavaScript code snippet using playwright-internal
ESLintRuleExhaustiveDeps-test.js
Source:ESLintRuleExhaustiveDeps-test.js
...29 {30 code: `31 function MyComponent() {32 const local = {};33 useEffect(() => {34 console.log(local);35 });36 }37 `,38 },39 {40 code: `41 function MyComponent() {42 useEffect(() => {43 const local = {};44 console.log(local);45 }, []);46 }47 `,48 },49 {50 code: `51 function MyComponent() {52 const local = {};53 useEffect(() => {54 console.log(local);55 }, [local]);56 }57 `,58 },59 {60 // OK because `props` wasn't defined.61 // We don't technically know if `props` is supposed62 // to be an import that hasn't been added yet, or63 // a component-level variable. Ignore it until it64 // gets defined (a different rule would flag it anyway).65 code: `66 function MyComponent() {67 useEffect(() => {68 console.log(props.foo);69 }, []);70 }71 `,72 },73 {74 code: `75 function MyComponent() {76 const local1 = {};77 {78 const local2 = {};79 useEffect(() => {80 console.log(local1);81 console.log(local2);82 });83 }84 }85 `,86 },87 {88 code: `89 function MyComponent() {90 const local1 = {};91 {92 const local2 = {};93 useCallback(() => {94 console.log(local1);95 console.log(local2);96 }, [local1, local2]);97 }98 }99 `,100 },101 {102 code: `103 function MyComponent() {104 const local1 = {};105 function MyNestedComponent() {106 const local2 = {};107 useCallback(() => {108 console.log(local1);109 console.log(local2);110 }, [local2]);111 }112 }113 `,114 },115 {116 code: `117 function MyComponent() {118 const local = {};119 useEffect(() => {120 console.log(local);121 console.log(local);122 }, [local]);123 }124 `,125 },126 {127 code: `128 function MyComponent() {129 useEffect(() => {130 console.log(unresolved);131 }, []);132 }133 `,134 },135 {136 code: `137 function MyComponent() {138 const local = {};139 useEffect(() => {140 console.log(local);141 }, [,,,local,,,]);142 }143 `,144 },145 {146 // Regression test147 code: `148 function MyComponent({ foo }) {149 useEffect(() => {150 console.log(foo.length);151 }, [foo]);152 }153 `,154 },155 {156 // Regression test157 code: `158 function MyComponent({ foo }) {159 useEffect(() => {160 console.log(foo.length);161 console.log(foo.slice(0));162 }, [foo]);163 }164 `,165 },166 {167 // Regression test168 code: `169 function MyComponent({ history }) {170 useEffect(() => {171 return history.listen();172 }, [history]);173 }174 `,175 },176 {177 // Valid because they have meaning without deps.178 code: `179 function MyComponent(props) {180 useEffect(() => {});181 useLayoutEffect(() => {});182 useImperativeHandle(props.innerRef, () => {});183 }184 `,185 },186 {187 code: `188 function MyComponent(props) {189 useEffect(() => {190 console.log(props.foo);191 }, [props.foo]);192 }193 `,194 },195 {196 code: `197 function MyComponent(props) {198 useEffect(() => {199 console.log(props.foo);200 console.log(props.bar);201 }, [props.bar, props.foo]);202 }203 `,204 },205 {206 code: `207 function MyComponent(props) {208 useEffect(() => {209 console.log(props.foo);210 console.log(props.bar);211 }, [props.foo, props.bar]);212 }213 `,214 },215 {216 code: `217 function MyComponent(props) {218 const local = {};219 useEffect(() => {220 console.log(props.foo);221 console.log(props.bar);222 console.log(local);223 }, [props.foo, props.bar, local]);224 }225 `,226 },227 {228 // [props, props.foo] is technically unnecessary ('props' covers 'props.foo').229 // However, it's valid for effects to over-specify their deps.230 // So we don't warn about this. We *would* warn about useMemo/useCallback.231 code: `232 function MyComponent(props) {233 const local = {};234 useEffect(() => {235 console.log(props.foo);236 console.log(props.bar);237 }, [props, props.foo]);238 let color = {}239 useEffect(() => {240 console.log(props.foo.bar.baz);241 console.log(color);242 }, [props.foo, props.foo.bar.baz, color]);243 }244 `,245 },246 {247 code: `248 function MyComponent(props) {249 useCustomEffect(() => {250 console.log(props.foo);251 });252 }253 `,254 options: [{additionalHooks: 'useCustomEffect'}],255 },256 {257 code: `258 function MyComponent(props) {259 useCustomEffect(() => {260 console.log(props.foo);261 }, [props.foo]);262 }263 `,264 options: [{additionalHooks: 'useCustomEffect'}],265 },266 {267 code: `268 function MyComponent(props) {269 useCustomEffect(() => {270 console.log(props.foo);271 }, []);272 }273 `,274 options: [{additionalHooks: 'useAnotherEffect'}],275 },276 {277 // Valid because we don't care about hooks outside of components.278 code: `279 const local = {};280 useEffect(() => {281 console.log(local);282 }, []);283 `,284 },285 {286 // Valid because we don't care about hooks outside of components.287 code: `288 const local1 = {};289 {290 const local2 = {};291 useEffect(() => {292 console.log(local1);293 console.log(local2);294 }, []);295 }296 `,297 },298 {299 code: `300 function MyComponent() {301 const ref = useRef();302 useEffect(() => {303 console.log(ref.current);304 }, [ref]);305 }306 `,307 },308 {309 code: `310 function MyComponent() {311 const ref = useRef();312 useEffect(() => {313 console.log(ref.current);314 }, []);315 }316 `,317 },318 {319 code: `320 function MyComponent({ maybeRef2, foo }) {321 const definitelyRef1 = useRef();322 const definitelyRef2 = useRef();323 const maybeRef1 = useSomeOtherRefyThing();324 const [state1, setState1] = useState();325 const [state2, setState2] = React.useState();326 const [state3, dispatch1] = useReducer();327 const [state4, dispatch2] = React.useReducer();328 const [state5, maybeSetState] = useFunnyState();329 const [state6, maybeDispatch] = useFunnyReducer();330 const mySetState = useCallback(() => {}, []);331 let myDispatch = useCallback(() => {}, []);332 useEffect(() => {333 // Known to be static334 console.log(definitelyRef1.current);335 console.log(definitelyRef2.current);336 console.log(maybeRef1.current);337 console.log(maybeRef2.current);338 setState1();339 setState2();340 dispatch1();341 dispatch2();342 // Dynamic343 console.log(state1);344 console.log(state2);345 console.log(state3);346 console.log(state4);347 console.log(state5);348 console.log(state6);349 mySetState();350 myDispatch();351 // Not sure; assume dynamic352 maybeSetState();353 maybeDispatch();354 }, [355 // Dynamic356 state1, state2, state3, state4, state5, state6,357 maybeRef1, maybeRef2,358 // Not sure; assume dynamic359 mySetState, myDispatch,360 maybeSetState, maybeDispatch361 // In this test, we don't specify static deps.362 // That should be okay.363 ]);364 }365 `,366 },367 {368 code: `369 function MyComponent({ maybeRef2 }) {370 const definitelyRef1 = useRef();371 const definitelyRef2 = useRef();372 const maybeRef1 = useSomeOtherRefyThing();373 const [state1, setState1] = useState();374 const [state2, setState2] = React.useState();375 const [state3, dispatch1] = useReducer();376 const [state4, dispatch2] = React.useReducer();377 const [state5, maybeSetState] = useFunnyState();378 const [state6, maybeDispatch] = useFunnyReducer();379 const mySetState = useCallback(() => {}, []);380 let myDispatch = useCallback(() => {}, []);381 useEffect(() => {382 // Known to be static383 console.log(definitelyRef1.current);384 console.log(definitelyRef2.current);385 console.log(maybeRef1.current);386 console.log(maybeRef2.current);387 setState1();388 setState2();389 dispatch1();390 dispatch2();391 // Dynamic392 console.log(state1);393 console.log(state2);394 console.log(state3);395 console.log(state4);396 console.log(state5);397 console.log(state6);398 mySetState();399 myDispatch();400 // Not sure; assume dynamic401 maybeSetState();402 maybeDispatch();403 }, [404 // Dynamic405 state1, state2, state3, state4, state5, state6,406 maybeRef1, maybeRef2,407 // Not sure; assume dynamic408 mySetState, myDispatch,409 maybeSetState, maybeDispatch,410 // In this test, we specify static deps.411 // That should be okay too!412 definitelyRef1, definitelyRef2, setState1, setState2, dispatch1, dispatch2413 ]);414 }415 `,416 },417 {418 code: `419 const MyComponent = forwardRef((props, ref) => {420 useImperativeHandle(ref, () => ({421 focus() {422 alert(props.hello);423 }424 }))425 });426 `,427 },428 {429 code: `430 const MyComponent = forwardRef((props, ref) => {431 useImperativeHandle(ref, () => ({432 focus() {433 alert(props.hello);434 }435 }), [props.hello])436 });437 `,438 },439 {440 // This is not ideal but warning would likely create441 // too many false positives. We do, however, prevent442 // direct assignments.443 code: `444 function MyComponent(props) {445 let obj = {};446 useEffect(() => {447 obj.foo = true;448 }, [obj]);449 }450 `,451 },452 {453 // Valid because we assign ref.current454 // ourselves. Therefore it's likely not455 // a ref managed by React.456 code: `457 function MyComponent() {458 const myRef = useRef();459 useEffect(() => {460 const handleMove = () => {};461 myRef.current = {};462 return () => {463 console.log(myRef.current.toString())464 };465 }, []);466 return <div />;467 }468 `,469 },470 {471 // Valid because we assign ref.current472 // ourselves. Therefore it's likely not473 // a ref managed by React.474 code: `475 function useMyThing(myRef) {476 useEffect(() => {477 const handleMove = () => {};478 myRef.current = {};479 return () => {480 console.log(myRef.current.toString())481 };482 }, [myRef]);483 }484 `,485 },486 {487 // Valid because the ref is captured.488 code: `489 function MyComponent() {490 const myRef = useRef();491 useEffect(() => {492 const handleMove = () => {};493 const node = myRef.current;494 node.addEventListener('mousemove', handleMove);495 return () => node.removeEventListener('mousemove', handleMove);496 }, []);497 return <div ref={myRef} />;498 }499 `,500 },501 {502 // Valid because the ref is captured.503 code: `504 function useMyThing(myRef) {505 const myRef = useRef();506 useEffect(() => {507 const handleMove = () => {};508 const node = myRef.current;509 node.addEventListener('mousemove', handleMove);510 return () => node.removeEventListener('mousemove', handleMove);511 }, [myRef]);512 return <div ref={myRef} />;513 }514 `,515 },516 {517 // Valid because it's not an effect.518 code: `519 function useMyThing(myRef) {520 useCallback(() => {521 const handleMouse = () => {};522 myRef.current.addEventListener('mousemove', handleMouse);523 myRef.current.addEventListener('mousein', handleMouse);524 return function() {525 setTimeout(() => {526 myRef.current.removeEventListener('mousemove', handleMouse);527 myRef.current.removeEventListener('mousein', handleMouse);528 });529 }530 }, [myRef]);531 }532 `,533 },534 {535 // Valid because we read ref.current in a function that isn't cleanup.536 code: `537 function useMyThing() {538 const myRef = useRef();539 useEffect(() => {540 const handleMove = () => {541 console.log(myRef.current)542 };543 window.addEventListener('mousemove', handleMove);544 return () => window.removeEventListener('mousemove', handleMove);545 }, []);546 return <div ref={myRef} />;547 }548 `,549 },550 {551 // Valid because we read ref.current in a function that isn't cleanup.552 code: `553 function useMyThing() {554 const myRef = useRef();555 useEffect(() => {556 const handleMove = () => {557 return () => window.removeEventListener('mousemove', handleMove);558 };559 window.addEventListener('mousemove', handleMove);560 return () => {};561 }, []);562 return <div ref={myRef} />;563 }564 `,565 },566 {567 // Valid because it's a primitive constant.568 code: `569 function MyComponent() {570 const local1 = 42;571 const local2 = '42';572 const local3 = null;573 useEffect(() => {574 console.log(local1);575 console.log(local2);576 console.log(local3);577 }, []);578 }579 `,580 },581 {582 // It's not a mistake to specify constant values though.583 code: `584 function MyComponent() {585 const local1 = 42;586 const local2 = '42';587 const local3 = null;588 useEffect(() => {589 console.log(local1);590 console.log(local2);591 console.log(local3);592 }, [local1, local2, local3]);593 }594 `,595 },596 {597 // It is valid for effects to over-specify their deps.598 code: `599 function MyComponent(props) {600 const local = props.local;601 useEffect(() => {}, [local]);602 }603 `,604 },605 {606 // Valid even though activeTab is "unused".607 // We allow over-specifying deps for effects, but not callbacks or memo.608 code: `609 function Foo({ activeTab }) {610 useEffect(() => {611 window.scrollTo(0, 0);612 }, [activeTab]);613 }614 `,615 },616 {617 // It is valid to specify broader effect deps than strictly necessary.618 // Don't warn for this.619 code: `620 function MyComponent(props) {621 useEffect(() => {622 console.log(props.foo.bar.baz);623 }, [props]);624 useEffect(() => {625 console.log(props.foo.bar.baz);626 }, [props.foo]);627 useEffect(() => {628 console.log(props.foo.bar.baz);629 }, [props.foo.bar]);630 useEffect(() => {631 console.log(props.foo.bar.baz);632 }, [props.foo.bar.baz]);633 }634 `,635 },636 {637 // It is *also* valid to specify broader memo/callback deps than strictly necessary.638 // Don't warn for this either.639 code: `640 function MyComponent(props) {641 const fn = useCallback(() => {642 console.log(props.foo.bar.baz);643 }, [props]);644 const fn2 = useCallback(() => {645 console.log(props.foo.bar.baz);646 }, [props.foo]);647 const fn3 = useMemo(() => {648 console.log(props.foo.bar.baz);649 }, [props.foo.bar]);650 const fn4 = useMemo(() => {651 console.log(props.foo.bar.baz);652 }, [props.foo.bar.baz]);653 }654 `,655 },656 {657 // Declaring handleNext is optional because658 // it doesn't use anything in the function scope.659 code: `660 function MyComponent(props) {661 function handleNext1() {662 console.log('hello');663 }664 const handleNext2 = () => {665 console.log('hello');666 };667 let handleNext3 = function() {668 console.log('hello');669 };670 useEffect(() => {671 return Store.subscribe(handleNext1);672 }, []);673 useLayoutEffect(() => {674 return Store.subscribe(handleNext2);675 }, []);676 useMemo(() => {677 return Store.subscribe(handleNext3);678 }, []);679 }680 `,681 },682 {683 // Declaring handleNext is optional because684 // it doesn't use anything in the function scope.685 code: `686 function MyComponent(props) {687 function handleNext() {688 console.log('hello');689 }690 useEffect(() => {691 return Store.subscribe(handleNext);692 }, []);693 useLayoutEffect(() => {694 return Store.subscribe(handleNext);695 }, []);696 useMemo(() => {697 return Store.subscribe(handleNext);698 }, []);699 }700 `,701 },702 {703 // Declaring handleNext is optional because704 // everything they use is fully static.705 code: `706 function MyComponent(props) {707 let [, setState] = useState();708 let [, dispatch] = React.useReducer();709 function handleNext1(value) {710 let value2 = value * 100;711 setState(value2);712 console.log('hello');713 }714 const handleNext2 = (value) => {715 setState(foo(value));716 console.log('hello');717 };718 let handleNext3 = function(value) {719 console.log(value);720 dispatch({ type: 'x', value });721 };722 useEffect(() => {723 return Store.subscribe(handleNext1);724 }, []);725 useLayoutEffect(() => {726 return Store.subscribe(handleNext2);727 }, []);728 useMemo(() => {729 return Store.subscribe(handleNext3);730 }, []);731 }732 `,733 },734 {735 code: `736 function useInterval(callback, delay) {737 const savedCallback = useRef();738 useEffect(() => {739 savedCallback.current = callback;740 });741 useEffect(() => {742 function tick() {743 savedCallback.current();744 }745 if (delay !== null) {746 let id = setInterval(tick, delay);747 return () => clearInterval(id);748 }749 }, [delay]);750 }751 `,752 },753 {754 code: `755 function Counter() {756 const [count, setCount] = useState(0);757 useEffect(() => {758 let id = setInterval(() => {759 setCount(c => c + 1);760 }, 1000);761 return () => clearInterval(id);762 }, []);763 return <h1>{count}</h1>;764 }765 `,766 },767 {768 code: `769 function Counter() {770 const [count, setCount] = useState(0);771 function tick() {772 setCount(c => c + 1);773 }774 useEffect(() => {775 let id = setInterval(() => {776 tick();777 }, 1000);778 return () => clearInterval(id);779 }, []);780 return <h1>{count}</h1>;781 }782 `,783 },784 {785 code: `786 function Counter() {787 const [count, dispatch] = useReducer((state, action) => {788 if (action === 'inc') {789 return state + 1;790 }791 }, 0);792 useEffect(() => {793 let id = setInterval(() => {794 dispatch('inc');795 }, 1000);796 return () => clearInterval(id);797 }, []);798 return <h1>{count}</h1>;799 }800 `,801 },802 {803 code: `804 function Counter() {805 const [count, dispatch] = useReducer((state, action) => {806 if (action === 'inc') {807 return state + 1;808 }809 }, 0);810 const tick = () => {811 dispatch('inc');812 };813 useEffect(() => {814 let id = setInterval(tick, 1000);815 return () => clearInterval(id);816 }, []);817 return <h1>{count}</h1>;818 }819 `,820 },821 {822 // Regression test for a crash823 code: `824 function Podcasts() {825 useEffect(() => {826 setPodcasts([]);827 }, []);828 let [podcasts, setPodcasts] = useState(null);829 }830 `,831 },832 {833 code: `834 function withFetch(fetchPodcasts) {835 return function Podcasts({ id }) {836 let [podcasts, setPodcasts] = useState(null);837 useEffect(() => {838 fetchPodcasts(id).then(setPodcasts);839 }, [id]);840 }841 }842 `,843 },844 {845 code: `846 function Podcasts({ id }) {847 let [podcasts, setPodcasts] = useState(null);848 useEffect(() => {849 function doFetch({ fetchPodcasts }) {850 fetchPodcasts(id).then(setPodcasts);851 }852 doFetch({ fetchPodcasts: API.fetchPodcasts });853 }, [id]);854 }855 `,856 },857 {858 code: `859 function Counter() {860 let [count, setCount] = useState(0);861 function increment(x) {862 return x + 1;863 }864 useEffect(() => {865 let id = setInterval(() => {866 setCount(increment);867 }, 1000);868 return () => clearInterval(id);869 }, []);870 return <h1>{count}</h1>;871 }872 `,873 },874 {875 code: `876 function Counter() {877 let [count, setCount] = useState(0);878 function increment(x) {879 return x + 1;880 }881 useEffect(() => {882 let id = setInterval(() => {883 setCount(count => increment(count));884 }, 1000);885 return () => clearInterval(id);886 }, []);887 return <h1>{count}</h1>;888 }889 `,890 },891 {892 code: `893 import increment from './increment';894 function Counter() {895 let [count, setCount] = useState(0);896 useEffect(() => {897 let id = setInterval(() => {898 setCount(count => count + increment);899 }, 1000);900 return () => clearInterval(id);901 }, []);902 return <h1>{count}</h1>;903 }904 `,905 },906 {907 code: `908 function withStuff(increment) {909 return function Counter() {910 let [count, setCount] = useState(0);911 useEffect(() => {912 let id = setInterval(() => {913 setCount(count => count + increment);914 }, 1000);915 return () => clearInterval(id);916 }, []);917 return <h1>{count}</h1>;918 }919 }920 `,921 },922 {923 code: `924 function App() {925 const [query, setQuery] = useState('react');926 const [state, setState] = useState(null);927 useEffect(() => {928 let ignore = false;929 fetchSomething();930 async function fetchSomething() {931 const result = await (await fetch('http://hn.algolia.com/api/v1/search?query=' + query)).json();932 if (!ignore) setState(result);933 }934 return () => { ignore = true; };935 }, [query]);936 return (937 <>938 <input value={query} onChange={e => setQuery(e.target.value)} />939 {JSON.stringify(state)}940 </>941 );942 }943 `,944 },945 {946 code: `947 function Example() {948 const foo = useCallback(() => {949 foo();950 }, []);951 }952 `,953 },954 {955 code: `956 function Example({ prop }) {957 const foo = useCallback(() => {958 if (prop) {959 foo();960 }961 }, [prop]);962 }963 `,964 },965 {966 code: `967 function Hello() {968 const [state, setState] = useState(0);969 useEffect(() => {970 const handleResize = () => setState(window.innerWidth);971 window.addEventListener('resize', handleResize);972 return () => window.removeEventListener('resize', handleResize);973 });974 }975 `,976 },977 // Ignore Generic Type Variables for arrow functions978 {979 code: `980 function Example({ prop }) {981 const bar = useEffect(<T>(a: T): Hello => {982 prop();983 }, [prop]);984 }985 `,986 },987 ],988 invalid: [989 {990 code: `991 function MyComponent() {992 const local = {};993 useEffect(() => {994 console.log(local);995 }, []);996 }997 `,998 output: `999 function MyComponent() {1000 const local = {};1001 useEffect(() => {1002 console.log(local);1003 }, [local]);1004 }1005 `,1006 errors: [1007 "React Hook useEffect has a missing dependency: 'local'. " +1008 'Either include it or remove the dependency array.',1009 ],1010 },1011 {1012 // Note: we *could* detect it's a primitive and never assigned1013 // even though it's not a constant -- but we currently don't.1014 // So this is an error.1015 code: `1016 function MyComponent() {1017 let local = 42;1018 useEffect(() => {1019 console.log(local);1020 }, []);1021 }1022 `,1023 output: `1024 function MyComponent() {1025 let local = 42;1026 useEffect(() => {1027 console.log(local);1028 }, [local]);1029 }1030 `,1031 errors: [1032 "React Hook useEffect has a missing dependency: 'local'. " +1033 'Either include it or remove the dependency array.',1034 ],1035 },1036 {1037 // Regexes are literals but potentially stateful.1038 code: `1039 function MyComponent() {1040 const local = /foo/;1041 useEffect(() => {1042 console.log(local);1043 }, []);1044 }1045 `,1046 output: `1047 function MyComponent() {1048 const local = /foo/;1049 useEffect(() => {1050 console.log(local);1051 }, [local]);1052 }1053 `,1054 errors: [1055 "React Hook useEffect has a missing dependency: 'local'. " +1056 'Either include it or remove the dependency array.',1057 ],1058 },1059 {1060 // Invalid because they don't have a meaning without deps.1061 code: `1062 function MyComponent(props) {1063 const value = useMemo(() => { return 2*2; });1064 const fn = useCallback(() => { alert('foo'); });1065 }1066 `,1067 // We don't know what you meant.1068 output: `1069 function MyComponent(props) {1070 const value = useMemo(() => { return 2*2; });1071 const fn = useCallback(() => { alert('foo'); });1072 }1073 `,1074 errors: [1075 'React Hook useMemo does nothing when called with only one argument. ' +1076 'Did you forget to pass an array of dependencies?',1077 'React Hook useCallback does nothing when called with only one argument. ' +1078 'Did you forget to pass an array of dependencies?',1079 ],1080 },1081 {1082 // Regression test1083 code: `1084 function MyComponent() {1085 const local = {};1086 useEffect(() => {1087 if (true) {1088 console.log(local);1089 }1090 }, []);1091 }1092 `,1093 output: `1094 function MyComponent() {1095 const local = {};1096 useEffect(() => {1097 if (true) {1098 console.log(local);1099 }1100 }, [local]);1101 }1102 `,1103 errors: [1104 "React Hook useEffect has a missing dependency: 'local'. " +1105 'Either include it or remove the dependency array.',1106 ],1107 },1108 {1109 // Regression test1110 code: `1111 function MyComponent() {1112 const local = {};1113 useEffect(() => {1114 try {1115 console.log(local);1116 } finally {}1117 }, []);1118 }1119 `,1120 output: `1121 function MyComponent() {1122 const local = {};1123 useEffect(() => {1124 try {1125 console.log(local);1126 } finally {}1127 }, [local]);1128 }1129 `,1130 errors: [1131 "React Hook useEffect has a missing dependency: 'local'. " +1132 'Either include it or remove the dependency array.',1133 ],1134 },1135 {1136 // Regression test1137 code: `1138 function MyComponent() {1139 const local = {};1140 useEffect(() => {1141 function inner() {1142 console.log(local);1143 }1144 inner();1145 }, []);1146 }1147 `,1148 output: `1149 function MyComponent() {1150 const local = {};1151 useEffect(() => {1152 function inner() {1153 console.log(local);1154 }1155 inner();1156 }, [local]);1157 }1158 `,1159 errors: [1160 "React Hook useEffect has a missing dependency: 'local'. " +1161 'Either include it or remove the dependency array.',1162 ],1163 },1164 {1165 code: `1166 function MyComponent() {1167 const local1 = {};1168 {1169 const local2 = {};1170 useEffect(() => {1171 console.log(local1);1172 console.log(local2);1173 }, []);1174 }1175 }1176 `,1177 output: `1178 function MyComponent() {1179 const local1 = {};1180 {1181 const local2 = {};1182 useEffect(() => {1183 console.log(local1);1184 console.log(local2);1185 }, [local1, local2]);1186 }1187 }1188 `,1189 errors: [1190 "React Hook useEffect has missing dependencies: 'local1' and 'local2'. " +1191 'Either include them or remove the dependency array.',1192 ],1193 },1194 {1195 code: `1196 function MyComponent() {1197 const local1 = {};1198 const local2 = {};1199 useEffect(() => {1200 console.log(local1);1201 console.log(local2);1202 }, [local1]);1203 }1204 `,1205 output: `1206 function MyComponent() {1207 const local1 = {};1208 const local2 = {};1209 useEffect(() => {1210 console.log(local1);1211 console.log(local2);1212 }, [local1, local2]);1213 }1214 `,1215 errors: [1216 "React Hook useEffect has a missing dependency: 'local2'. " +1217 'Either include it or remove the dependency array.',1218 ],1219 },1220 {1221 code: `1222 function MyComponent() {1223 const local1 = {};1224 const local2 = {};1225 useMemo(() => {1226 console.log(local1);1227 }, [local1, local2]);1228 }1229 `,1230 output: `1231 function MyComponent() {1232 const local1 = {};1233 const local2 = {};1234 useMemo(() => {1235 console.log(local1);1236 }, [local1]);1237 }1238 `,1239 errors: [1240 "React Hook useMemo has an unnecessary dependency: 'local2'. " +1241 'Either exclude it or remove the dependency array.',1242 ],1243 },1244 {1245 code: `1246 function MyComponent() {1247 const local1 = {};1248 function MyNestedComponent() {1249 const local2 = {};1250 useCallback(() => {1251 console.log(local1);1252 console.log(local2);1253 }, [local1]);1254 }1255 }1256 `,1257 output: `1258 function MyComponent() {1259 const local1 = {};1260 function MyNestedComponent() {1261 const local2 = {};1262 useCallback(() => {1263 console.log(local1);1264 console.log(local2);1265 }, [local2]);1266 }1267 }1268 `,1269 errors: [1270 "React Hook useCallback has a missing dependency: 'local2'. " +1271 'Either include it or remove the dependency array. ' +1272 "Outer scope values like 'local1' aren't valid dependencies " +1273 "because mutating them doesn't re-render the component.",1274 ],1275 },1276 {1277 code: `1278 function MyComponent() {1279 const local = {};1280 useEffect(() => {1281 console.log(local);1282 console.log(local);1283 }, []);1284 }1285 `,1286 output: `1287 function MyComponent() {1288 const local = {};1289 useEffect(() => {1290 console.log(local);1291 console.log(local);1292 }, [local]);1293 }1294 `,1295 errors: [1296 "React Hook useEffect has a missing dependency: 'local'. " +1297 'Either include it or remove the dependency array.',1298 ],1299 },1300 {1301 code: `1302 function MyComponent() {1303 const local = {};1304 useEffect(() => {1305 console.log(local);1306 console.log(local);1307 }, [local, local]);1308 }1309 `,1310 output: `1311 function MyComponent() {1312 const local = {};1313 useEffect(() => {1314 console.log(local);1315 console.log(local);1316 }, [local]);1317 }1318 `,1319 errors: [1320 "React Hook useEffect has a duplicate dependency: 'local'. " +1321 'Either omit it or remove the dependency array.',1322 ],1323 },1324 {1325 code: `1326 function MyComponent() {1327 useCallback(() => {}, [window]);1328 }1329 `,1330 output: `1331 function MyComponent() {1332 useCallback(() => {}, []);1333 }1334 `,1335 errors: [1336 "React Hook useCallback has an unnecessary dependency: 'window'. " +1337 'Either exclude it or remove the dependency array. ' +1338 "Outer scope values like 'window' aren't valid dependencies " +1339 "because mutating them doesn't re-render the component.",1340 ],1341 },1342 {1343 // It is not valid for useCallback to specify extraneous deps1344 // because it doesn't serve as a side effect trigger unlike useEffect.1345 code: `1346 function MyComponent(props) {1347 let local = props.foo;1348 useCallback(() => {}, [local]);1349 }1350 `,1351 output: `1352 function MyComponent(props) {1353 let local = props.foo;1354 useCallback(() => {}, []);1355 }1356 `,1357 errors: [1358 "React Hook useCallback has an unnecessary dependency: 'local'. " +1359 'Either exclude it or remove the dependency array.',1360 ],1361 },1362 {1363 code: `1364 function MyComponent({ history }) {1365 useEffect(() => {1366 return history.listen();1367 }, []);1368 }1369 `,1370 output: `1371 function MyComponent({ history }) {1372 useEffect(() => {1373 return history.listen();1374 }, [history]);1375 }1376 `,1377 errors: [1378 "React Hook useEffect has a missing dependency: 'history'. " +1379 'Either include it or remove the dependency array.',1380 ],1381 },1382 {1383 code: `1384 function MyComponent({ history }) {1385 useEffect(() => {1386 return [1387 history.foo.bar[2].dobedo.listen(),1388 history.foo.bar().dobedo.listen[2]1389 ];1390 }, []);1391 }1392 `,1393 output: `1394 function MyComponent({ history }) {1395 useEffect(() => {1396 return [1397 history.foo.bar[2].dobedo.listen(),1398 history.foo.bar().dobedo.listen[2]1399 ];1400 }, [history.foo]);1401 }1402 `,1403 errors: [1404 "React Hook useEffect has a missing dependency: 'history.foo'. " +1405 'Either include it or remove the dependency array.',1406 ],1407 },1408 {1409 code: `1410 function MyComponent() {1411 useEffect(() => {}, ['foo']);1412 }1413 `,1414 // TODO: we could autofix this.1415 output: `1416 function MyComponent() {1417 useEffect(() => {}, ['foo']);1418 }1419 `,1420 errors: [1421 // Don't assume user meant `foo` because it's not used in the effect.1422 "The 'foo' literal is not a valid dependency because it never changes. " +1423 'You can safely remove it.',1424 ],1425 },1426 {1427 code: `1428 function MyComponent({ foo, bar, baz }) {1429 useEffect(() => {1430 console.log(foo, bar, baz);1431 }, ['foo', 'bar']);1432 }1433 `,1434 output: `1435 function MyComponent({ foo, bar, baz }) {1436 useEffect(() => {1437 console.log(foo, bar, baz);1438 }, [bar, baz, foo]);1439 }1440 `,1441 errors: [1442 "React Hook useEffect has missing dependencies: 'bar', 'baz', and 'foo'. " +1443 'Either include them or remove the dependency array.',1444 "The 'foo' literal is not a valid dependency because it never changes. " +1445 'Did you mean to include foo in the array instead?',1446 "The 'bar' literal is not a valid dependency because it never changes. " +1447 'Did you mean to include bar in the array instead?',1448 ],1449 },1450 {1451 code: `1452 function MyComponent({ foo, bar, baz }) {1453 useEffect(() => {1454 console.log(foo, bar, baz);1455 }, [42, false, null]);1456 }1457 `,1458 output: `1459 function MyComponent({ foo, bar, baz }) {1460 useEffect(() => {1461 console.log(foo, bar, baz);1462 }, [bar, baz, foo]);1463 }1464 `,1465 errors: [1466 "React Hook useEffect has missing dependencies: 'bar', 'baz', and 'foo'. " +1467 'Either include them or remove the dependency array.',1468 'The 42 literal is not a valid dependency because it never changes. You can safely remove it.',1469 'The false literal is not a valid dependency because it never changes. You can safely remove it.',1470 'The null literal is not a valid dependency because it never changes. You can safely remove it.',1471 ],1472 },1473 {1474 code: `1475 function MyComponent() {1476 const dependencies = [];1477 useEffect(() => {}, dependencies);1478 }1479 `,1480 output: `1481 function MyComponent() {1482 const dependencies = [];1483 useEffect(() => {}, dependencies);1484 }1485 `,1486 errors: [1487 'React Hook useEffect was passed a dependency list that is not an ' +1488 "array literal. This means we can't statically verify whether you've " +1489 'passed the correct dependencies.',1490 ],1491 },1492 {1493 code: `1494 function MyComponent() {1495 const local = {};1496 const dependencies = [local];1497 useEffect(() => {1498 console.log(local);1499 }, dependencies);1500 }1501 `,1502 // TODO: should this autofix or bail out?1503 output: `1504 function MyComponent() {1505 const local = {};1506 const dependencies = [local];1507 useEffect(() => {1508 console.log(local);1509 }, [local]);1510 }1511 `,1512 errors: [1513 'React Hook useEffect was passed a dependency list that is not an ' +1514 "array literal. This means we can't statically verify whether you've " +1515 'passed the correct dependencies.',1516 "React Hook useEffect has a missing dependency: 'local'. " +1517 'Either include it or remove the dependency array.',1518 ],1519 },1520 {1521 code: `1522 function MyComponent() {1523 const local = {};1524 const dependencies = [local];1525 useEffect(() => {1526 console.log(local);1527 }, [...dependencies]);1528 }1529 `,1530 // TODO: should this autofix or bail out?1531 output: `1532 function MyComponent() {1533 const local = {};1534 const dependencies = [local];1535 useEffect(() => {1536 console.log(local);1537 }, [local]);1538 }1539 `,1540 errors: [1541 "React Hook useEffect has a missing dependency: 'local'. " +1542 'Either include it or remove the dependency array.',1543 'React Hook useEffect has a spread element in its dependency array. ' +1544 "This means we can't statically verify whether you've passed the " +1545 'correct dependencies.',1546 ],1547 },1548 {1549 code: `1550 function MyComponent() {1551 const local = {};1552 useEffect(() => {1553 console.log(local);1554 }, [local, ...dependencies]);1555 }1556 `,1557 output: `1558 function MyComponent() {1559 const local = {};1560 useEffect(() => {1561 console.log(local);1562 }, [local, ...dependencies]);1563 }1564 `,1565 errors: [1566 'React Hook useEffect has a spread element in its dependency array. ' +1567 "This means we can't statically verify whether you've passed the " +1568 'correct dependencies.',1569 ],1570 },1571 {1572 code: `1573 function MyComponent() {1574 const local = {};1575 useEffect(() => {1576 console.log(local);1577 }, [computeCacheKey(local)]);1578 }1579 `,1580 // TODO: I'm not sure this is a good idea.1581 // Maybe bail out?1582 output: `1583 function MyComponent() {1584 const local = {};1585 useEffect(() => {1586 console.log(local);1587 }, [local]);1588 }1589 `,1590 errors: [1591 "React Hook useEffect has a missing dependency: 'local'. " +1592 'Either include it or remove the dependency array.',1593 'React Hook useEffect has a complex expression in the dependency array. ' +1594 'Extract it to a separate variable so it can be statically checked.',1595 ],1596 },1597 {1598 code: `1599 function MyComponent(props) {1600 useEffect(() => {1601 console.log(props.items[0]);1602 }, [props.items[0]]);1603 }1604 `,1605 output: `1606 function MyComponent(props) {1607 useEffect(() => {1608 console.log(props.items[0]);1609 }, [props.items]);1610 }1611 `,1612 errors: [1613 "React Hook useEffect has a missing dependency: 'props.items'. " +1614 'Either include it or remove the dependency array.',1615 'React Hook useEffect has a complex expression in the dependency array. ' +1616 'Extract it to a separate variable so it can be statically checked.',1617 ],1618 },1619 {1620 code: `1621 function MyComponent(props) {1622 useEffect(() => {1623 console.log(props.items[0]);1624 }, [props.items, props.items[0]]);1625 }1626 `,1627 // TODO: ideally autofix would remove the bad expression?1628 output: `1629 function MyComponent(props) {1630 useEffect(() => {1631 console.log(props.items[0]);1632 }, [props.items, props.items[0]]);1633 }1634 `,1635 errors: [1636 'React Hook useEffect has a complex expression in the dependency array. ' +1637 'Extract it to a separate variable so it can be statically checked.',1638 ],1639 },1640 {1641 code: `1642 function MyComponent({ items }) {1643 useEffect(() => {1644 console.log(items[0]);1645 }, [items[0]]);1646 }1647 `,1648 output: `1649 function MyComponent({ items }) {1650 useEffect(() => {1651 console.log(items[0]);1652 }, [items]);1653 }1654 `,1655 errors: [1656 "React Hook useEffect has a missing dependency: 'items'. " +1657 'Either include it or remove the dependency array.',1658 'React Hook useEffect has a complex expression in the dependency array. ' +1659 'Extract it to a separate variable so it can be statically checked.',1660 ],1661 },1662 {1663 code: `1664 function MyComponent({ items }) {1665 useEffect(() => {1666 console.log(items[0]);1667 }, [items, items[0]]);1668 }1669 `,1670 // TODO: ideally autofix would remove the bad expression?1671 output: `1672 function MyComponent({ items }) {1673 useEffect(() => {1674 console.log(items[0]);1675 }, [items, items[0]]);1676 }1677 `,1678 errors: [1679 'React Hook useEffect has a complex expression in the dependency array. ' +1680 'Extract it to a separate variable so it can be statically checked.',1681 ],1682 },1683 {1684 // It is not valid for useCallback to specify extraneous deps1685 // because it doesn't serve as a side effect trigger unlike useEffect.1686 // However, we generally allow specifying *broader* deps as escape hatch.1687 // So while [props, props.foo] is unnecessary, 'props' wins here as the1688 // broader one, and this is why 'props.foo' is reported as unnecessary.1689 code: `1690 function MyComponent(props) {1691 const local = {};1692 useCallback(() => {1693 console.log(props.foo);1694 console.log(props.bar);1695 }, [props, props.foo]);1696 }1697 `,1698 output: `1699 function MyComponent(props) {1700 const local = {};1701 useCallback(() => {1702 console.log(props.foo);1703 console.log(props.bar);1704 }, [props]);1705 }1706 `,1707 errors: [1708 "React Hook useCallback has an unnecessary dependency: 'props.foo'. " +1709 'Either exclude it or remove the dependency array.',1710 ],1711 },1712 {1713 // Since we don't have 'props' in the list, we'll suggest narrow dependencies.1714 code: `1715 function MyComponent(props) {1716 const local = {};1717 useCallback(() => {1718 console.log(props.foo);1719 console.log(props.bar);1720 }, []);1721 }1722 `,1723 output: `1724 function MyComponent(props) {1725 const local = {};1726 useCallback(() => {1727 console.log(props.foo);1728 console.log(props.bar);1729 }, [props.bar, props.foo]);1730 }1731 `,1732 errors: [1733 "React Hook useCallback has missing dependencies: 'props.bar' and 'props.foo'. " +1734 'Either include them or remove the dependency array.',1735 ],1736 },1737 {1738 // Effects are allowed to over-specify deps. We'll complain about missing1739 // 'local', but we won't remove the already-specified 'local.id' from your list.1740 code: `1741 function MyComponent() {1742 const local = {id: 42};1743 useEffect(() => {1744 console.log(local);1745 }, [local.id]);1746 }1747 `,1748 output: `1749 function MyComponent() {1750 const local = {id: 42};1751 useEffect(() => {1752 console.log(local);1753 }, [local, local.id]);1754 }1755 `,1756 errors: [1757 "React Hook useEffect has a missing dependency: 'local'. " +1758 'Either include it or remove the dependency array.',1759 ],1760 },1761 {1762 // Callbacks are not allowed to over-specify deps. So we'll complain about missing1763 // 'local' and we will also *remove* 'local.id' from your list.1764 code: `1765 function MyComponent() {1766 const local = {id: 42};1767 const fn = useCallback(() => {1768 console.log(local);1769 }, [local.id]);1770 }1771 `,1772 output: `1773 function MyComponent() {1774 const local = {id: 42};1775 const fn = useCallback(() => {1776 console.log(local);1777 }, [local]);1778 }1779 `,1780 errors: [1781 "React Hook useCallback has a missing dependency: 'local'. " +1782 'Either include it or remove the dependency array.',1783 ],1784 },1785 {1786 // Callbacks are not allowed to over-specify deps. So we'll complain about1787 // the unnecessary 'local.id'.1788 code: `1789 function MyComponent() {1790 const local = {id: 42};1791 const fn = useCallback(() => {1792 console.log(local);1793 }, [local.id, local]);1794 }1795 `,1796 output: `1797 function MyComponent() {1798 const local = {id: 42};1799 const fn = useCallback(() => {1800 console.log(local);1801 }, [local]);1802 }1803 `,1804 errors: [1805 "React Hook useCallback has an unnecessary dependency: 'local.id'. " +1806 'Either exclude it or remove the dependency array.',1807 ],1808 },1809 {1810 code: `1811 function MyComponent(props) {1812 const fn = useCallback(() => {1813 console.log(props.foo.bar.baz);1814 }, []);1815 }1816 `,1817 output: `1818 function MyComponent(props) {1819 const fn = useCallback(() => {1820 console.log(props.foo.bar.baz);1821 }, [props.foo.bar.baz]);1822 }1823 `,1824 errors: [1825 "React Hook useCallback has a missing dependency: 'props.foo.bar.baz'. " +1826 'Either include it or remove the dependency array.',1827 ],1828 },1829 {1830 code: `1831 function MyComponent(props) {1832 let color = {}1833 const fn = useCallback(() => {1834 console.log(props.foo.bar.baz);1835 console.log(color);1836 }, [props.foo, props.foo.bar.baz]);1837 }1838 `,1839 output: `1840 function MyComponent(props) {1841 let color = {}1842 const fn = useCallback(() => {1843 console.log(props.foo.bar.baz);1844 console.log(color);1845 }, [color, props.foo.bar.baz]);1846 }1847 `,1848 errors: [1849 "React Hook useCallback has a missing dependency: 'color'. " +1850 'Either include it or remove the dependency array.',1851 ],1852 },1853 {1854 // Callbacks are not allowed to over-specify deps. So one of these is extra.1855 // However, it *is* allowed to specify broader deps then strictly necessary.1856 // So in this case we ask you to remove 'props.foo.bar.baz' because 'props.foo'1857 // already covers it, and having both is unnecessary.1858 // TODO: maybe consider suggesting a narrower one by default in these cases.1859 code: `1860 function MyComponent(props) {1861 const fn = useCallback(() => {1862 console.log(props.foo.bar.baz);1863 }, [props.foo.bar.baz, props.foo]);1864 }1865 `,1866 output: `1867 function MyComponent(props) {1868 const fn = useCallback(() => {1869 console.log(props.foo.bar.baz);1870 }, [props.foo]);1871 }1872 `,1873 errors: [1874 "React Hook useCallback has an unnecessary dependency: 'props.foo.bar.baz'. " +1875 'Either exclude it or remove the dependency array.',1876 ],1877 },1878 {1879 code: `1880 function MyComponent(props) {1881 const fn = useCallback(() => {1882 console.log(props.foo.bar.baz);1883 console.log(props.foo.fizz.bizz);1884 }, []);1885 }1886 `,1887 output: `1888 function MyComponent(props) {1889 const fn = useCallback(() => {1890 console.log(props.foo.bar.baz);1891 console.log(props.foo.fizz.bizz);1892 }, [props.foo.bar.baz, props.foo.fizz.bizz]);1893 }1894 `,1895 errors: [1896 "React Hook useCallback has missing dependencies: 'props.foo.bar.baz' and 'props.foo.fizz.bizz'. " +1897 'Either include them or remove the dependency array.',1898 ],1899 },1900 {1901 // Normally we allow specifying deps too broadly.1902 // So we'd be okay if 'props.foo.bar' was there rather than 'props.foo.bar.baz'.1903 // However, 'props.foo.bar.baz' is missing. So we know there is a mistake.1904 // When we're sure there is a mistake, for callbacks we will rebuild the list1905 // from scratch. This will set the user on a better path by default.1906 // This is why we end up with just 'props.foo.bar', and not them both.1907 code: `1908 function MyComponent(props) {1909 const fn = useCallback(() => {1910 console.log(props.foo.bar);1911 }, [props.foo.bar.baz]);1912 }1913 `,1914 output: `1915 function MyComponent(props) {1916 const fn = useCallback(() => {1917 console.log(props.foo.bar);1918 }, [props.foo.bar]);1919 }1920 `,1921 errors: [1922 "React Hook useCallback has a missing dependency: 'props.foo.bar'. " +1923 'Either include it or remove the dependency array.',1924 ],1925 },1926 {1927 code: `1928 function MyComponent(props) {1929 const fn = useCallback(() => {1930 console.log(props);1931 console.log(props.hello);1932 }, [props.foo.bar.baz]);1933 }1934 `,1935 output: `1936 function MyComponent(props) {1937 const fn = useCallback(() => {1938 console.log(props);1939 console.log(props.hello);1940 }, [props]);1941 }1942 `,1943 errors: [1944 "React Hook useCallback has a missing dependency: 'props'. " +1945 'Either include it or remove the dependency array.',1946 ],1947 },1948 {1949 code: `1950 function MyComponent() {1951 const local = {};1952 useEffect(() => {1953 console.log(local);1954 }, [local, local]);1955 }1956 `,1957 output: `1958 function MyComponent() {1959 const local = {};1960 useEffect(() => {1961 console.log(local);1962 }, [local]);1963 }1964 `,1965 errors: [1966 "React Hook useEffect has a duplicate dependency: 'local'. " +1967 'Either omit it or remove the dependency array.',1968 ],1969 },1970 {1971 code: `1972 function MyComponent() {1973 const local1 = {};1974 useCallback(() => {1975 const local1 = {};1976 console.log(local1);1977 }, [local1]);1978 }1979 `,1980 output: `1981 function MyComponent() {1982 const local1 = {};1983 useCallback(() => {1984 const local1 = {};1985 console.log(local1);1986 }, []);1987 }1988 `,1989 errors: [1990 "React Hook useCallback has an unnecessary dependency: 'local1'. " +1991 'Either exclude it or remove the dependency array.',1992 ],1993 },1994 {1995 code: `1996 function MyComponent() {1997 const local1 = {};1998 useCallback(() => {}, [local1]);1999 }2000 `,2001 output: `2002 function MyComponent() {2003 const local1 = {};2004 useCallback(() => {}, []);2005 }2006 `,2007 errors: [2008 "React Hook useCallback has an unnecessary dependency: 'local1'. " +2009 'Either exclude it or remove the dependency array.',2010 ],2011 },2012 {2013 code: `2014 function MyComponent(props) {2015 useEffect(() => {2016 console.log(props.foo);2017 }, []);2018 }2019 `,2020 output: `2021 function MyComponent(props) {2022 useEffect(() => {2023 console.log(props.foo);2024 }, [props.foo]);2025 }2026 `,2027 errors: [2028 "React Hook useEffect has a missing dependency: 'props.foo'. " +2029 'Either include it or remove the dependency array.',2030 ],2031 },2032 {2033 code: `2034 function MyComponent(props) {2035 useEffect(() => {2036 console.log(props.foo);2037 console.log(props.bar);2038 }, []);2039 }2040 `,2041 output: `2042 function MyComponent(props) {2043 useEffect(() => {2044 console.log(props.foo);2045 console.log(props.bar);2046 }, [props.bar, props.foo]);2047 }2048 `,2049 errors: [2050 "React Hook useEffect has missing dependencies: 'props.bar' and 'props.foo'. " +2051 'Either include them or remove the dependency array.',2052 ],2053 },2054 {2055 code: `2056 function MyComponent(props) {2057 let a, b, c, d, e, f, g;2058 useEffect(() => {2059 console.log(b, e, d, c, a, g, f);2060 }, [c, a, g]);2061 }2062 `,2063 // Don't alphabetize if it wasn't alphabetized in the first place.2064 output: `2065 function MyComponent(props) {2066 let a, b, c, d, e, f, g;2067 useEffect(() => {2068 console.log(b, e, d, c, a, g, f);2069 }, [c, a, g, b, e, d, f]);2070 }2071 `,2072 errors: [2073 "React Hook useEffect has missing dependencies: 'b', 'd', 'e', and 'f'. " +2074 'Either include them or remove the dependency array.',2075 ],2076 },2077 {2078 code: `2079 function MyComponent(props) {2080 let a, b, c, d, e, f, g;2081 useEffect(() => {2082 console.log(b, e, d, c, a, g, f);2083 }, [a, c, g]);2084 }2085 `,2086 // Alphabetize if it was alphabetized.2087 output: `2088 function MyComponent(props) {2089 let a, b, c, d, e, f, g;2090 useEffect(() => {2091 console.log(b, e, d, c, a, g, f);2092 }, [a, b, c, d, e, f, g]);2093 }2094 `,2095 errors: [2096 "React Hook useEffect has missing dependencies: 'b', 'd', 'e', and 'f'. " +2097 'Either include them or remove the dependency array.',2098 ],2099 },2100 {2101 code: `2102 function MyComponent(props) {2103 let a, b, c, d, e, f, g;2104 useEffect(() => {2105 console.log(b, e, d, c, a, g, f);2106 }, []);2107 }2108 `,2109 // Alphabetize if it was empty.2110 output: `2111 function MyComponent(props) {2112 let a, b, c, d, e, f, g;2113 useEffect(() => {2114 console.log(b, e, d, c, a, g, f);2115 }, [a, b, c, d, e, f, g]);2116 }2117 `,2118 errors: [2119 "React Hook useEffect has missing dependencies: 'a', 'b', 'c', 'd', 'e', 'f', and 'g'. " +2120 'Either include them or remove the dependency array.',2121 ],2122 },2123 {2124 code: `2125 function MyComponent(props) {2126 const local = {};2127 useEffect(() => {2128 console.log(props.foo);2129 console.log(props.bar);2130 console.log(local);2131 }, []);2132 }2133 `,2134 output: `2135 function MyComponent(props) {2136 const local = {};2137 useEffect(() => {2138 console.log(props.foo);2139 console.log(props.bar);2140 console.log(local);2141 }, [local, props.bar, props.foo]);2142 }2143 `,2144 errors: [2145 "React Hook useEffect has missing dependencies: 'local', 'props.bar', and 'props.foo'. " +2146 'Either include them or remove the dependency array.',2147 ],2148 },2149 {2150 code: `2151 function MyComponent(props) {2152 const local = {};2153 useEffect(() => {2154 console.log(props.foo);2155 console.log(props.bar);2156 console.log(local);2157 }, [props]);2158 }2159 `,2160 output: `2161 function MyComponent(props) {2162 const local = {};2163 useEffect(() => {2164 console.log(props.foo);2165 console.log(props.bar);2166 console.log(local);2167 }, [local, props]);2168 }2169 `,2170 errors: [2171 "React Hook useEffect has a missing dependency: 'local'. " +2172 'Either include it or remove the dependency array.',2173 ],2174 },2175 {2176 code: `2177 function MyComponent(props) {2178 useEffect(() => {2179 console.log(props.foo);2180 }, []);2181 useCallback(() => {2182 console.log(props.foo);2183 }, []);2184 useMemo(() => {2185 console.log(props.foo);2186 }, []);2187 React.useEffect(() => {2188 console.log(props.foo);2189 }, []);2190 React.useCallback(() => {2191 console.log(props.foo);2192 }, []);2193 React.useMemo(() => {2194 console.log(props.foo);2195 }, []);2196 React.notReactiveHook(() => {2197 console.log(props.foo);2198 }, []);2199 }2200 `,2201 output: `2202 function MyComponent(props) {2203 useEffect(() => {2204 console.log(props.foo);2205 }, [props.foo]);2206 useCallback(() => {2207 console.log(props.foo);2208 }, [props.foo]);2209 useMemo(() => {2210 console.log(props.foo);2211 }, [props.foo]);2212 React.useEffect(() => {2213 console.log(props.foo);2214 }, [props.foo]);2215 React.useCallback(() => {2216 console.log(props.foo);2217 }, [props.foo]);2218 React.useMemo(() => {2219 console.log(props.foo);2220 }, [props.foo]);2221 React.notReactiveHook(() => {2222 console.log(props.foo);2223 }, []);2224 }2225 `,2226 errors: [2227 "React Hook useEffect has a missing dependency: 'props.foo'. " +2228 'Either include it or remove the dependency array.',2229 "React Hook useCallback has a missing dependency: 'props.foo'. " +2230 'Either include it or remove the dependency array.',2231 "React Hook useMemo has a missing dependency: 'props.foo'. " +2232 'Either include it or remove the dependency array.',2233 "React Hook React.useEffect has a missing dependency: 'props.foo'. " +2234 'Either include it or remove the dependency array.',2235 "React Hook React.useCallback has a missing dependency: 'props.foo'. " +2236 'Either include it or remove the dependency array.',2237 "React Hook React.useMemo has a missing dependency: 'props.foo'. " +2238 'Either include it or remove the dependency array.',2239 ],2240 },2241 {2242 code: `2243 function MyComponent(props) {2244 useCustomEffect(() => {2245 console.log(props.foo);2246 }, []);2247 useEffect(() => {2248 console.log(props.foo);2249 }, []);2250 React.useEffect(() => {2251 console.log(props.foo);2252 }, []);2253 React.useCustomEffect(() => {2254 console.log(props.foo);2255 }, []);2256 }2257 `,2258 output: `2259 function MyComponent(props) {2260 useCustomEffect(() => {2261 console.log(props.foo);2262 }, [props.foo]);2263 useEffect(() => {2264 console.log(props.foo);2265 }, [props.foo]);2266 React.useEffect(() => {2267 console.log(props.foo);2268 }, [props.foo]);2269 React.useCustomEffect(() => {2270 console.log(props.foo);2271 }, []);2272 }2273 `,2274 options: [{additionalHooks: 'useCustomEffect'}],2275 errors: [2276 "React Hook useCustomEffect has a missing dependency: 'props.foo'. " +2277 'Either include it or remove the dependency array.',2278 "React Hook useEffect has a missing dependency: 'props.foo'. " +2279 'Either include it or remove the dependency array.',2280 "React Hook React.useEffect has a missing dependency: 'props.foo'. " +2281 'Either include it or remove the dependency array.',2282 ],2283 },2284 {2285 code: `2286 function MyComponent() {2287 const local = {};2288 useEffect(() => {2289 console.log(local);2290 }, [a ? local : b]);2291 }2292 `,2293 // TODO: should we bail out instead?2294 output: `2295 function MyComponent() {2296 const local = {};2297 useEffect(() => {2298 console.log(local);2299 }, [local]);2300 }2301 `,2302 errors: [2303 "React Hook useEffect has a missing dependency: 'local'. " +2304 'Either include it or remove the dependency array.',2305 'React Hook useEffect has a complex expression in the dependency array. ' +2306 'Extract it to a separate variable so it can be statically checked.',2307 ],2308 },2309 {2310 code: `2311 function MyComponent() {2312 const local = {};2313 useEffect(() => {2314 console.log(local);2315 }, [a && local]);2316 }2317 `,2318 // TODO: should we bail out instead?2319 output: `2320 function MyComponent() {2321 const local = {};2322 useEffect(() => {2323 console.log(local);2324 }, [local]);2325 }2326 `,2327 errors: [2328 "React Hook useEffect has a missing dependency: 'local'. " +2329 'Either include it or remove the dependency array.',2330 'React Hook useEffect has a complex expression in the dependency array. ' +2331 'Extract it to a separate variable so it can be statically checked.',2332 ],2333 },2334 {2335 code: `2336 function MyComponent() {2337 const ref = useRef();2338 const [state, setState] = useState();2339 useEffect(() => {2340 ref.current = {};2341 setState(state + 1);2342 }, []);2343 }2344 `,2345 output: `2346 function MyComponent() {2347 const ref = useRef();2348 const [state, setState] = useState();2349 useEffect(() => {2350 ref.current = {};2351 setState(state + 1);2352 }, [state]);2353 }2354 `,2355 errors: [2356 "React Hook useEffect has a missing dependency: 'state'. " +2357 'Either include it or remove the dependency array. ' +2358 `You can also do a functional update 'setState(s => ...)' ` +2359 `if you only need 'state' in the 'setState' call.`,2360 ],2361 },2362 {2363 code: `2364 function MyComponent() {2365 const ref = useRef();2366 const [state, setState] = useState();2367 useEffect(() => {2368 ref.current = {};2369 setState(state + 1);2370 }, [ref]);2371 }2372 `,2373 // We don't ask to remove static deps but don't add them either.2374 // Don't suggest removing "ref" (it's fine either way)2375 // but *do* add "state". *Don't* add "setState" ourselves.2376 output: `2377 function MyComponent() {2378 const ref = useRef();2379 const [state, setState] = useState();2380 useEffect(() => {2381 ref.current = {};2382 setState(state + 1);2383 }, [ref, state]);2384 }2385 `,2386 errors: [2387 "React Hook useEffect has a missing dependency: 'state'. " +2388 'Either include it or remove the dependency array. ' +2389 `You can also do a functional update 'setState(s => ...)' ` +2390 `if you only need 'state' in the 'setState' call.`,2391 ],2392 },2393 {2394 code: `2395 function MyComponent(props) {2396 const ref1 = useRef();2397 const ref2 = useRef();2398 useEffect(() => {2399 ref1.current.focus();2400 console.log(ref2.current.textContent);2401 alert(props.someOtherRefs.current.innerHTML);2402 fetch(props.color);2403 }, []);2404 }2405 `,2406 output: `2407 function MyComponent(props) {2408 const ref1 = useRef();2409 const ref2 = useRef();2410 useEffect(() => {2411 ref1.current.focus();2412 console.log(ref2.current.textContent);2413 alert(props.someOtherRefs.current.innerHTML);2414 fetch(props.color);2415 }, [props.color, props.someOtherRefs]);2416 }2417 `,2418 errors: [2419 "React Hook useEffect has missing dependencies: 'props.color' and 'props.someOtherRefs'. " +2420 'Either include them or remove the dependency array.',2421 ],2422 },2423 {2424 code: `2425 function MyComponent(props) {2426 const ref1 = useRef();2427 const ref2 = useRef();2428 useEffect(() => {2429 ref1.current.focus();2430 console.log(ref2.current.textContent);2431 alert(props.someOtherRefs.current.innerHTML);2432 fetch(props.color);2433 }, [ref1.current, ref2.current, props.someOtherRefs, props.color]);2434 }2435 `,2436 output: `2437 function MyComponent(props) {2438 const ref1 = useRef();2439 const ref2 = useRef();2440 useEffect(() => {2441 ref1.current.focus();2442 console.log(ref2.current.textContent);2443 alert(props.someOtherRefs.current.innerHTML);2444 fetch(props.color);2445 }, [props.someOtherRefs, props.color]);2446 }2447 `,2448 errors: [2449 "React Hook useEffect has unnecessary dependencies: 'ref1.current' and 'ref2.current'. " +2450 'Either exclude them or remove the dependency array. ' +2451 "Mutable values like 'ref1.current' aren't valid dependencies " +2452 "because mutating them doesn't re-render the component.",2453 ],2454 },2455 {2456 code: `2457 function MyComponent() {2458 const ref = useRef();2459 useEffect(() => {2460 console.log(ref.current);2461 }, [ref.current]);2462 }2463 `,2464 output: `2465 function MyComponent() {2466 const ref = useRef();2467 useEffect(() => {2468 console.log(ref.current);2469 }, []);2470 }2471 `,2472 errors: [2473 "React Hook useEffect has an unnecessary dependency: 'ref.current'. " +2474 'Either exclude it or remove the dependency array. ' +2475 "Mutable values like 'ref.current' aren't valid dependencies " +2476 "because mutating them doesn't re-render the component.",2477 ],2478 },2479 {2480 code: `2481 function MyComponent({ activeTab }) {2482 const ref1 = useRef();2483 const ref2 = useRef();2484 useEffect(() => {2485 ref1.current.scrollTop = 0;2486 ref2.current.scrollTop = 0;2487 }, [ref1.current, ref2.current, activeTab]);2488 }2489 `,2490 output: `2491 function MyComponent({ activeTab }) {2492 const ref1 = useRef();2493 const ref2 = useRef();2494 useEffect(() => {2495 ref1.current.scrollTop = 0;2496 ref2.current.scrollTop = 0;2497 }, [activeTab]);2498 }2499 `,2500 errors: [2501 "React Hook useEffect has unnecessary dependencies: 'ref1.current' and 'ref2.current'. " +2502 'Either exclude them or remove the dependency array. ' +2503 "Mutable values like 'ref1.current' aren't valid dependencies " +2504 "because mutating them doesn't re-render the component.",2505 ],2506 },2507 {2508 code: `2509 function MyComponent({ activeTab, initY }) {2510 const ref1 = useRef();2511 const ref2 = useRef();2512 const fn = useCallback(() => {2513 ref1.current.scrollTop = initY;2514 ref2.current.scrollTop = initY;2515 }, [ref1.current, ref2.current, activeTab, initY]);2516 }2517 `,2518 output: `2519 function MyComponent({ activeTab, initY }) {2520 const ref1 = useRef();2521 const ref2 = useRef();2522 const fn = useCallback(() => {2523 ref1.current.scrollTop = initY;2524 ref2.current.scrollTop = initY;2525 }, [initY]);2526 }2527 `,2528 errors: [2529 "React Hook useCallback has unnecessary dependencies: 'activeTab', 'ref1.current', and 'ref2.current'. " +2530 'Either exclude them or remove the dependency array. ' +2531 "Mutable values like 'ref1.current' aren't valid dependencies " +2532 "because mutating them doesn't re-render the component.",2533 ],2534 },2535 {2536 code: `2537 function MyComponent() {2538 const ref = useRef();2539 useEffect(() => {2540 console.log(ref.current);2541 }, [ref.current, ref]);2542 }2543 `,2544 output: `2545 function MyComponent() {2546 const ref = useRef();2547 useEffect(() => {2548 console.log(ref.current);2549 }, [ref]);2550 }2551 `,2552 errors: [2553 "React Hook useEffect has an unnecessary dependency: 'ref.current'. " +2554 'Either exclude it or remove the dependency array. ' +2555 "Mutable values like 'ref.current' aren't valid dependencies " +2556 "because mutating them doesn't re-render the component.",2557 ],2558 },2559 {2560 code: `2561 const MyComponent = forwardRef((props, ref) => {2562 useImperativeHandle(ref, () => ({2563 focus() {2564 alert(props.hello);2565 }2566 }), [])2567 });2568 `,2569 output: `2570 const MyComponent = forwardRef((props, ref) => {2571 useImperativeHandle(ref, () => ({2572 focus() {2573 alert(props.hello);2574 }2575 }), [props.hello])2576 });2577 `,2578 errors: [2579 "React Hook useImperativeHandle has a missing dependency: 'props.hello'. " +2580 'Either include it or remove the dependency array.',2581 ],2582 },2583 {2584 code: `2585 function MyComponent(props) {2586 useEffect(() => {2587 if (props.onChange) {2588 props.onChange();2589 }2590 }, []);2591 }2592 `,2593 output: `2594 function MyComponent(props) {2595 useEffect(() => {2596 if (props.onChange) {2597 props.onChange();2598 }2599 }, [props]);2600 }2601 `,2602 errors: [2603 "React Hook useEffect has a missing dependency: 'props'. " +2604 'Either include it or remove the dependency array. ' +2605 `However, 'props' will change when *any* prop changes, so the ` +2606 `preferred fix is to destructure the 'props' object outside ` +2607 `of the useEffect call and refer to those specific ` +2608 `props inside useEffect.`,2609 ],2610 },2611 {2612 code: `2613 function MyComponent(props) {2614 useEffect(() => {2615 function play() {2616 props.onPlay();2617 }2618 function pause() {2619 props.onPause();2620 }2621 }, []);2622 }2623 `,2624 output: `2625 function MyComponent(props) {2626 useEffect(() => {2627 function play() {2628 props.onPlay();2629 }2630 function pause() {2631 props.onPause();2632 }2633 }, [props]);2634 }2635 `,2636 errors: [2637 "React Hook useEffect has a missing dependency: 'props'. " +2638 'Either include it or remove the dependency array. ' +2639 `However, 'props' will change when *any* prop changes, so the ` +2640 `preferred fix is to destructure the 'props' object outside ` +2641 `of the useEffect call and refer to those specific ` +2642 `props inside useEffect.`,2643 ],2644 },2645 {2646 code: `2647 function MyComponent(props) {2648 useEffect(() => {2649 if (props.foo.onChange) {2650 props.foo.onChange();2651 }2652 }, []);2653 }2654 `,2655 output: `2656 function MyComponent(props) {2657 useEffect(() => {2658 if (props.foo.onChange) {2659 props.foo.onChange();2660 }2661 }, [props.foo]);2662 }2663 `,2664 errors: [2665 "React Hook useEffect has a missing dependency: 'props.foo'. " +2666 'Either include it or remove the dependency array.',2667 ],2668 },2669 {2670 code: `2671 function MyComponent(props) {2672 useEffect(() => {2673 props.onChange();2674 if (props.foo.onChange) {2675 props.foo.onChange();2676 }2677 }, []);2678 }2679 `,2680 output: `2681 function MyComponent(props) {2682 useEffect(() => {2683 props.onChange();2684 if (props.foo.onChange) {2685 props.foo.onChange();2686 }2687 }, [props]);2688 }2689 `,2690 errors: [2691 "React Hook useEffect has a missing dependency: 'props'. " +2692 'Either include it or remove the dependency array. ' +2693 `However, 'props' will change when *any* prop changes, so the ` +2694 `preferred fix is to destructure the 'props' object outside ` +2695 `of the useEffect call and refer to those specific ` +2696 `props inside useEffect.`,2697 ],2698 },2699 {2700 code: `2701 function MyComponent(props) {2702 const [skillsCount] = useState();2703 useEffect(() => {2704 if (skillsCount === 0 && !props.isEditMode) {2705 props.toggleEditMode();2706 }2707 }, [skillsCount, props.isEditMode, props.toggleEditMode]);2708 }2709 `,2710 output: `2711 function MyComponent(props) {2712 const [skillsCount] = useState();2713 useEffect(() => {2714 if (skillsCount === 0 && !props.isEditMode) {2715 props.toggleEditMode();2716 }2717 }, [skillsCount, props.isEditMode, props.toggleEditMode, props]);2718 }2719 `,2720 errors: [2721 "React Hook useEffect has a missing dependency: 'props'. " +2722 'Either include it or remove the dependency array. ' +2723 `However, 'props' will change when *any* prop changes, so the ` +2724 `preferred fix is to destructure the 'props' object outside ` +2725 `of the useEffect call and refer to those specific ` +2726 `props inside useEffect.`,2727 ],2728 },2729 {2730 code: `2731 function MyComponent(props) {2732 const [skillsCount] = useState();2733 useEffect(() => {2734 if (skillsCount === 0 && !props.isEditMode) {2735 props.toggleEditMode();2736 }2737 }, []);2738 }2739 `,2740 output: `2741 function MyComponent(props) {2742 const [skillsCount] = useState();2743 useEffect(() => {2744 if (skillsCount === 0 && !props.isEditMode) {2745 props.toggleEditMode();2746 }2747 }, [props, skillsCount]);2748 }2749 `,2750 errors: [2751 "React Hook useEffect has missing dependencies: 'props' and 'skillsCount'. " +2752 'Either include them or remove the dependency array. ' +2753 `However, 'props' will change when *any* prop changes, so the ` +2754 `preferred fix is to destructure the 'props' object outside ` +2755 `of the useEffect call and refer to those specific ` +2756 `props inside useEffect.`,2757 ],2758 },2759 {2760 code: `2761 function MyComponent(props) {2762 useEffect(() => {2763 externalCall(props);2764 props.onChange();2765 }, []);2766 }2767 `,2768 output: `2769 function MyComponent(props) {2770 useEffect(() => {2771 externalCall(props);2772 props.onChange();2773 }, [props]);2774 }2775 `,2776 // Don't suggest to destructure props here since you can't.2777 errors: [2778 "React Hook useEffect has a missing dependency: 'props'. " +2779 'Either include it or remove the dependency array.',2780 ],2781 },2782 {2783 code: `2784 function MyComponent(props) {2785 useEffect(() => {2786 props.onChange();2787 externalCall(props);2788 }, []);2789 }2790 `,2791 output: `2792 function MyComponent(props) {2793 useEffect(() => {2794 props.onChange();2795 externalCall(props);2796 }, [props]);2797 }2798 `,2799 // Don't suggest to destructure props here since you can't.2800 errors: [2801 "React Hook useEffect has a missing dependency: 'props'. " +2802 'Either include it or remove the dependency array.',2803 ],2804 },2805 {2806 code: `2807 function MyComponent(props) {2808 let value;2809 let value2;2810 let value3;2811 let value4;2812 let asyncValue;2813 useEffect(() => {2814 if (value4) {2815 value = {};2816 }2817 value2 = 100;2818 value = 43;2819 value4 = true;2820 console.log(value2);2821 console.log(value3);2822 setTimeout(() => {2823 asyncValue = 100;2824 });2825 }, []);2826 }2827 `,2828 // This is a separate warning unrelated to others.2829 // We could've made a separate rule for it but it's rare enough to name it.2830 // No autofix suggestion because the intent isn't clear.2831 output: `2832 function MyComponent(props) {2833 let value;2834 let value2;2835 let value3;2836 let value4;2837 let asyncValue;2838 useEffect(() => {2839 if (value4) {2840 value = {};2841 }2842 value2 = 100;2843 value = 43;2844 value4 = true;2845 console.log(value2);2846 console.log(value3);2847 setTimeout(() => {2848 asyncValue = 100;2849 });2850 }, []);2851 }2852 `,2853 errors: [2854 // value22855 `Assignments to the 'value2' variable from inside React Hook useEffect ` +2856 `will be lost after each render. To preserve the value over time, ` +2857 `store it in a useRef Hook and keep the mutable value in the '.current' property. ` +2858 `Otherwise, you can move this variable directly inside useEffect.`,2859 // value2860 `Assignments to the 'value' variable from inside React Hook useEffect ` +2861 `will be lost after each render. To preserve the value over time, ` +2862 `store it in a useRef Hook and keep the mutable value in the '.current' property. ` +2863 `Otherwise, you can move this variable directly inside useEffect.`,2864 // value42865 `Assignments to the 'value4' variable from inside React Hook useEffect ` +2866 `will be lost after each render. To preserve the value over time, ` +2867 `store it in a useRef Hook and keep the mutable value in the '.current' property. ` +2868 `Otherwise, you can move this variable directly inside useEffect.`,2869 // asyncValue2870 `Assignments to the 'asyncValue' variable from inside React Hook useEffect ` +2871 `will be lost after each render. To preserve the value over time, ` +2872 `store it in a useRef Hook and keep the mutable value in the '.current' property. ` +2873 `Otherwise, you can move this variable directly inside useEffect.`,2874 ],2875 },2876 {2877 code: `2878 function MyComponent(props) {2879 let value;2880 let value2;2881 let value3;2882 let asyncValue;2883 useEffect(() => {2884 value = {};2885 value2 = 100;2886 value = 43;2887 console.log(value2);2888 console.log(value3);2889 setTimeout(() => {2890 asyncValue = 100;2891 });2892 }, [value, value2, value3]);2893 }2894 `,2895 // This is a separate warning unrelated to others.2896 // We could've made a separate rule for it but it's rare enough to name it.2897 // No autofix suggestion because the intent isn't clear.2898 output: `2899 function MyComponent(props) {2900 let value;2901 let value2;2902 let value3;2903 let asyncValue;2904 useEffect(() => {2905 value = {};2906 value2 = 100;2907 value = 43;2908 console.log(value2);2909 console.log(value3);2910 setTimeout(() => {2911 asyncValue = 100;2912 });2913 }, [value, value2, value3]);2914 }2915 `,2916 errors: [2917 // value2918 `Assignments to the 'value' variable from inside React Hook useEffect ` +2919 `will be lost after each render. To preserve the value over time, ` +2920 `store it in a useRef Hook and keep the mutable value in the '.current' property. ` +2921 `Otherwise, you can move this variable directly inside useEffect.`,2922 // value22923 `Assignments to the 'value2' variable from inside React Hook useEffect ` +2924 `will be lost after each render. To preserve the value over time, ` +2925 `store it in a useRef Hook and keep the mutable value in the '.current' property. ` +2926 `Otherwise, you can move this variable directly inside useEffect.`,2927 // asyncValue2928 `Assignments to the 'asyncValue' variable from inside React Hook useEffect ` +2929 `will be lost after each render. To preserve the value over time, ` +2930 `store it in a useRef Hook and keep the mutable value in the '.current' property. ` +2931 `Otherwise, you can move this variable directly inside useEffect.`,2932 ],2933 },2934 {2935 code: `2936 function MyComponent() {2937 const myRef = useRef();2938 useEffect(() => {2939 const handleMove = () => {};2940 myRef.current.addEventListener('mousemove', handleMove);2941 return () => myRef.current.removeEventListener('mousemove', handleMove);2942 }, []);2943 return <div ref={myRef} />;2944 }2945 `,2946 output: `2947 function MyComponent() {2948 const myRef = useRef();2949 useEffect(() => {2950 const handleMove = () => {};2951 myRef.current.addEventListener('mousemove', handleMove);2952 return () => myRef.current.removeEventListener('mousemove', handleMove);2953 }, []);2954 return <div ref={myRef} />;2955 }2956 `,2957 errors: [2958 `The ref value 'myRef.current' will likely have changed by the time ` +2959 `this effect cleanup function runs. If this ref points to a node ` +2960 `rendered by React, copy 'myRef.current' to a variable inside the effect, ` +2961 `and use that variable in the cleanup function.`,2962 ],2963 },2964 {2965 code: `2966 function MyComponent() {2967 const myRef = useRef();2968 useEffect(() => {2969 const handleMove = () => {};2970 myRef.current.addEventListener('mousemove', handleMove);2971 return () => myRef.current.removeEventListener('mousemove', handleMove);2972 });2973 return <div ref={myRef} />;2974 }2975 `,2976 output: `2977 function MyComponent() {2978 const myRef = useRef();2979 useEffect(() => {2980 const handleMove = () => {};2981 myRef.current.addEventListener('mousemove', handleMove);2982 return () => myRef.current.removeEventListener('mousemove', handleMove);2983 });2984 return <div ref={myRef} />;2985 }2986 `,2987 errors: [2988 `The ref value 'myRef.current' will likely have changed by the time ` +2989 `this effect cleanup function runs. If this ref points to a node ` +2990 `rendered by React, copy 'myRef.current' to a variable inside the effect, ` +2991 `and use that variable in the cleanup function.`,2992 ],2993 },2994 {2995 code: `2996 function useMyThing(myRef) {2997 useEffect(() => {2998 const handleMove = () => {};2999 myRef.current.addEventListener('mousemove', handleMove);3000 return () => myRef.current.removeEventListener('mousemove', handleMove);3001 }, [myRef]);3002 }3003 `,3004 output: `3005 function useMyThing(myRef) {3006 useEffect(() => {3007 const handleMove = () => {};3008 myRef.current.addEventListener('mousemove', handleMove);3009 return () => myRef.current.removeEventListener('mousemove', handleMove);3010 }, [myRef]);3011 }3012 `,3013 errors: [3014 `The ref value 'myRef.current' will likely have changed by the time ` +3015 `this effect cleanup function runs. If this ref points to a node ` +3016 `rendered by React, copy 'myRef.current' to a variable inside the effect, ` +3017 `and use that variable in the cleanup function.`,3018 ],3019 },3020 {3021 code: `3022 function useMyThing(myRef) {3023 useEffect(() => {3024 const handleMouse = () => {};3025 myRef.current.addEventListener('mousemove', handleMouse);3026 myRef.current.addEventListener('mousein', handleMouse);3027 return function() {3028 setTimeout(() => {3029 myRef.current.removeEventListener('mousemove', handleMouse);3030 myRef.current.removeEventListener('mousein', handleMouse);3031 });3032 }3033 }, [myRef]);3034 }3035 `,3036 output: `3037 function useMyThing(myRef) {3038 useEffect(() => {3039 const handleMouse = () => {};3040 myRef.current.addEventListener('mousemove', handleMouse);3041 myRef.current.addEventListener('mousein', handleMouse);3042 return function() {3043 setTimeout(() => {3044 myRef.current.removeEventListener('mousemove', handleMouse);3045 myRef.current.removeEventListener('mousein', handleMouse);3046 });3047 }3048 }, [myRef]);3049 }3050 `,3051 errors: [3052 `The ref value 'myRef.current' will likely have changed by the time ` +3053 `this effect cleanup function runs. If this ref points to a node ` +3054 `rendered by React, copy 'myRef.current' to a variable inside the effect, ` +3055 `and use that variable in the cleanup function.`,3056 ],3057 },3058 {3059 code: `3060 function useMyThing(myRef, active) {3061 useEffect(() => {3062 const handleMove = () => {};3063 if (active) {3064 myRef.current.addEventListener('mousemove', handleMove);3065 return function() {3066 setTimeout(() => {3067 myRef.current.removeEventListener('mousemove', handleMove);3068 });3069 }3070 }3071 }, [myRef, active]);3072 }3073 `,3074 output: `3075 function useMyThing(myRef, active) {3076 useEffect(() => {3077 const handleMove = () => {};3078 if (active) {3079 myRef.current.addEventListener('mousemove', handleMove);3080 return function() {3081 setTimeout(() => {3082 myRef.current.removeEventListener('mousemove', handleMove);3083 });3084 }3085 }3086 }, [myRef, active]);3087 }3088 `,3089 errors: [3090 `The ref value 'myRef.current' will likely have changed by the time ` +3091 `this effect cleanup function runs. If this ref points to a node ` +3092 `rendered by React, copy 'myRef.current' to a variable inside the effect, ` +3093 `and use that variable in the cleanup function.`,3094 ],3095 },3096 {3097 // Autofix ignores constant primitives (leaving the ones that are there).3098 code: `3099 function MyComponent() {3100 const local1 = 42;3101 const local2 = '42';3102 const local3 = null;3103 const local4 = {};3104 useEffect(() => {3105 console.log(local1);3106 console.log(local2);3107 console.log(local3);3108 console.log(local4);3109 }, [local1, local3]);3110 }3111 `,3112 output: `3113 function MyComponent() {3114 const local1 = 42;3115 const local2 = '42';3116 const local3 = null;3117 const local4 = {};3118 useEffect(() => {3119 console.log(local1);3120 console.log(local2);3121 console.log(local3);3122 console.log(local4);3123 }, [local1, local3, local4]);3124 }3125 `,3126 errors: [3127 "React Hook useEffect has a missing dependency: 'local4'. " +3128 'Either include it or remove the dependency array.',3129 ],3130 },3131 {3132 code: `3133 function MyComponent() {3134 useEffect(() => {3135 window.scrollTo(0, 0);3136 }, [window]);3137 }3138 `,3139 errors: [3140 "React Hook useEffect has an unnecessary dependency: 'window'. " +3141 'Either exclude it or remove the dependency array. ' +3142 "Outer scope values like 'window' aren't valid dependencies " +3143 "because mutating them doesn't re-render the component.",3144 ],3145 },3146 {3147 code: `3148 import MutableStore from 'store';3149 function MyComponent() {3150 useEffect(() => {3151 console.log(MutableStore.hello);3152 }, [MutableStore.hello]);3153 }3154 `,3155 output: `3156 import MutableStore from 'store';3157 function MyComponent() {3158 useEffect(() => {3159 console.log(MutableStore.hello);3160 }, []);3161 }3162 `,3163 errors: [3164 "React Hook useEffect has an unnecessary dependency: 'MutableStore.hello'. " +3165 'Either exclude it or remove the dependency array. ' +3166 "Outer scope values like 'MutableStore.hello' aren't valid dependencies " +3167 "because mutating them doesn't re-render the component.",3168 ],3169 },3170 {3171 code: `3172 import MutableStore from 'store';3173 let z = {};3174 function MyComponent(props) {3175 let x = props.foo;3176 {3177 let y = props.bar;3178 useEffect(() => {3179 console.log(MutableStore.hello.world, props.foo, x, y, z, global.stuff);3180 }, [MutableStore.hello.world, props.foo, x, y, z, global.stuff]);3181 }3182 }3183 `,3184 output: `3185 import MutableStore from 'store';3186 let z = {};3187 function MyComponent(props) {3188 let x = props.foo;3189 {3190 let y = props.bar;3191 useEffect(() => {3192 console.log(MutableStore.hello.world, props.foo, x, y, z, global.stuff);3193 }, [props.foo, x, y]);3194 }3195 }3196 `,3197 errors: [3198 'React Hook useEffect has unnecessary dependencies: ' +3199 "'MutableStore.hello.world', 'global.stuff', and 'z'. " +3200 'Either exclude them or remove the dependency array. ' +3201 "Outer scope values like 'MutableStore.hello.world' aren't valid dependencies " +3202 "because mutating them doesn't re-render the component.",3203 ],3204 },3205 {3206 code: `3207 import MutableStore from 'store';3208 let z = {};3209 function MyComponent(props) {3210 let x = props.foo;3211 {3212 let y = props.bar;3213 useEffect(() => {3214 // nothing3215 }, [MutableStore.hello.world, props.foo, x, y, z, global.stuff]);3216 }3217 }3218 `,3219 // The output should contain the ones that are inside a component3220 // since there are legit reasons to over-specify them for effects.3221 output: `3222 import MutableStore from 'store';3223 let z = {};3224 function MyComponent(props) {3225 let x = props.foo;3226 {3227 let y = props.bar;3228 useEffect(() => {3229 // nothing3230 }, [props.foo, x, y]);3231 }3232 }3233 `,3234 errors: [3235 'React Hook useEffect has unnecessary dependencies: ' +3236 "'MutableStore.hello.world', 'global.stuff', and 'z'. " +3237 'Either exclude them or remove the dependency array. ' +3238 "Outer scope values like 'MutableStore.hello.world' aren't valid dependencies " +3239 "because mutating them doesn't re-render the component.",3240 ],3241 },3242 {3243 code: `3244 import MutableStore from 'store';3245 let z = {};3246 function MyComponent(props) {3247 let x = props.foo;3248 {3249 let y = props.bar;3250 const fn = useCallback(() => {3251 // nothing3252 }, [MutableStore.hello.world, props.foo, x, y, z, global.stuff]);3253 }3254 }3255 `,3256 output: `3257 import MutableStore from 'store';3258 let z = {};3259 function MyComponent(props) {3260 let x = props.foo;3261 {3262 let y = props.bar;3263 const fn = useCallback(() => {3264 // nothing3265 }, []);3266 }3267 }3268 `,3269 errors: [3270 'React Hook useCallback has unnecessary dependencies: ' +3271 "'MutableStore.hello.world', 'global.stuff', 'props.foo', 'x', 'y', and 'z'. " +3272 'Either exclude them or remove the dependency array. ' +3273 "Outer scope values like 'MutableStore.hello.world' aren't valid dependencies " +3274 "because mutating them doesn't re-render the component.",3275 ],3276 },3277 {3278 // Every almost-static function is tainted by a dynamic value.3279 code: `3280 function MyComponent(props) {3281 let [, setState] = useState();3282 let [, dispatch] = React.useReducer();3283 let taint = props.foo;3284 function handleNext1(value) {3285 let value2 = value * taint;3286 setState(value2);3287 console.log('hello');3288 }3289 const handleNext2 = (value) => {3290 setState(taint(value));3291 console.log('hello');3292 };3293 let handleNext3 = function(value) {3294 setTimeout(() => console.log(taint));3295 dispatch({ type: 'x', value });3296 };3297 useEffect(() => {3298 return Store.subscribe(handleNext1);3299 }, []);3300 useLayoutEffect(() => {3301 return Store.subscribe(handleNext2);3302 }, []);3303 useMemo(() => {3304 return Store.subscribe(handleNext3);3305 }, []);3306 }3307 `,3308 output: `3309 function MyComponent(props) {3310 let [, setState] = useState();3311 let [, dispatch] = React.useReducer();3312 let taint = props.foo;3313 function handleNext1(value) {3314 let value2 = value * taint;3315 setState(value2);3316 console.log('hello');3317 }3318 const handleNext2 = (value) => {3319 setState(taint(value));3320 console.log('hello');3321 };3322 let handleNext3 = function(value) {3323 setTimeout(() => console.log(taint));3324 dispatch({ type: 'x', value });3325 };3326 useEffect(() => {3327 return Store.subscribe(handleNext1);3328 }, [handleNext1]);3329 useLayoutEffect(() => {3330 return Store.subscribe(handleNext2);3331 }, [handleNext2]);3332 useMemo(() => {3333 return Store.subscribe(handleNext3);3334 }, [handleNext3]);3335 }3336 `,3337 errors: [3338 "React Hook useEffect has a missing dependency: 'handleNext1'. " +3339 'Either include it or remove the dependency array.',3340 "React Hook useLayoutEffect has a missing dependency: 'handleNext2'. " +3341 'Either include it or remove the dependency array.',3342 "React Hook useMemo has a missing dependency: 'handleNext3'. " +3343 'Either include it or remove the dependency array.',3344 ],3345 },3346 {3347 // Regression test3348 code: `3349 function MyComponent(props) {3350 let [, setState] = useState();3351 let [, dispatch] = React.useReducer();3352 let taint = props.foo;3353 // Shouldn't affect anything3354 function handleChange() {}3355 function handleNext1(value) {3356 let value2 = value * taint;3357 setState(value2);3358 console.log('hello');3359 }3360 const handleNext2 = (value) => {3361 setState(taint(value));3362 console.log('hello');3363 };3364 let handleNext3 = function(value) {3365 console.log(taint);3366 dispatch({ type: 'x', value });3367 };3368 useEffect(() => {3369 return Store.subscribe(handleNext1);3370 }, []);3371 useLayoutEffect(() => {3372 return Store.subscribe(handleNext2);3373 }, []);3374 useMemo(() => {3375 return Store.subscribe(handleNext3);3376 }, []);3377 }3378 `,3379 output: `3380 function MyComponent(props) {3381 let [, setState] = useState();3382 let [, dispatch] = React.useReducer();3383 let taint = props.foo;3384 // Shouldn't affect anything3385 function handleChange() {}3386 function handleNext1(value) {3387 let value2 = value * taint;3388 setState(value2);3389 console.log('hello');3390 }3391 const handleNext2 = (value) => {3392 setState(taint(value));3393 console.log('hello');3394 };3395 let handleNext3 = function(value) {3396 console.log(taint);3397 dispatch({ type: 'x', value });3398 };3399 useEffect(() => {3400 return Store.subscribe(handleNext1);3401 }, [handleNext1]);3402 useLayoutEffect(() => {3403 return Store.subscribe(handleNext2);3404 }, [handleNext2]);3405 useMemo(() => {3406 return Store.subscribe(handleNext3);3407 }, [handleNext3]);3408 }3409 `,3410 errors: [3411 "React Hook useEffect has a missing dependency: 'handleNext1'. " +3412 'Either include it or remove the dependency array.',3413 "React Hook useLayoutEffect has a missing dependency: 'handleNext2'. " +3414 'Either include it or remove the dependency array.',3415 "React Hook useMemo has a missing dependency: 'handleNext3'. " +3416 'Either include it or remove the dependency array.',3417 ],3418 },3419 {3420 // Regression test3421 code: `3422 function MyComponent(props) {3423 let [, setState] = useState();3424 let [, dispatch] = React.useReducer();3425 let taint = props.foo;3426 // Shouldn't affect anything3427 const handleChange = () => {};3428 function handleNext1(value) {3429 let value2 = value * taint;3430 setState(value2);3431 console.log('hello');3432 }3433 const handleNext2 = (value) => {3434 setState(taint(value));3435 console.log('hello');3436 };3437 let handleNext3 = function(value) {3438 console.log(taint);3439 dispatch({ type: 'x', value });3440 };3441 useEffect(() => {3442 return Store.subscribe(handleNext1);3443 }, []);3444 useLayoutEffect(() => {3445 return Store.subscribe(handleNext2);3446 }, []);3447 useMemo(() => {3448 return Store.subscribe(handleNext3);3449 }, []);3450 }3451 `,3452 output: `3453 function MyComponent(props) {3454 let [, setState] = useState();3455 let [, dispatch] = React.useReducer();3456 let taint = props.foo;3457 // Shouldn't affect anything3458 const handleChange = () => {};3459 function handleNext1(value) {3460 let value2 = value * taint;3461 setState(value2);3462 console.log('hello');3463 }3464 const handleNext2 = (value) => {3465 setState(taint(value));3466 console.log('hello');3467 };3468 let handleNext3 = function(value) {3469 console.log(taint);3470 dispatch({ type: 'x', value });3471 };3472 useEffect(() => {3473 return Store.subscribe(handleNext1);3474 }, [handleNext1]);3475 useLayoutEffect(() => {3476 return Store.subscribe(handleNext2);3477 }, [handleNext2]);3478 useMemo(() => {3479 return Store.subscribe(handleNext3);3480 }, [handleNext3]);3481 }3482 `,3483 errors: [3484 "React Hook useEffect has a missing dependency: 'handleNext1'. " +3485 'Either include it or remove the dependency array.',3486 "React Hook useLayoutEffect has a missing dependency: 'handleNext2'. " +3487 'Either include it or remove the dependency array.',3488 "React Hook useMemo has a missing dependency: 'handleNext3'. " +3489 'Either include it or remove the dependency array.',3490 ],3491 },3492 {3493 // Even if the function only references static values,3494 // once you specify it in deps, it will invalidate them.3495 code: `3496 function MyComponent(props) {3497 let [, setState] = useState();3498 function handleNext(value) {3499 setState(value);3500 }3501 useEffect(() => {3502 return Store.subscribe(handleNext);3503 }, [handleNext]);3504 }3505 `,3506 // Not gonna autofix a function definition3507 // because it's not always safe due to hoisting.3508 output: `3509 function MyComponent(props) {3510 let [, setState] = useState();3511 function handleNext(value) {3512 setState(value);3513 }3514 useEffect(() => {3515 return Store.subscribe(handleNext);3516 }, [handleNext]);3517 }3518 `,3519 errors: [3520 `The 'handleNext' function makes the dependencies of ` +3521 `useEffect Hook (at line 11) change on every render. ` +3522 `Move it inside the useEffect callback. Alternatively, ` +3523 `wrap the 'handleNext' definition into its own useCallback() Hook.`,3524 ],3525 },3526 {3527 // Even if the function only references static values,3528 // once you specify it in deps, it will invalidate them.3529 code: `3530 function MyComponent(props) {3531 let [, setState] = useState();3532 const handleNext = (value) => {3533 setState(value);3534 };3535 useEffect(() => {3536 return Store.subscribe(handleNext);3537 }, [handleNext]);3538 }3539 `,3540 // We don't autofix moving (too invasive). But that's the suggested fix3541 // when only effect uses this function. Otherwise, we'd useCallback.3542 output: `3543 function MyComponent(props) {3544 let [, setState] = useState();3545 const handleNext = (value) => {3546 setState(value);3547 };3548 useEffect(() => {3549 return Store.subscribe(handleNext);3550 }, [handleNext]);3551 }3552 `,3553 errors: [3554 `The 'handleNext' function makes the dependencies of ` +3555 `useEffect Hook (at line 11) change on every render. ` +3556 `Move it inside the useEffect callback. Alternatively, ` +3557 `wrap the 'handleNext' definition into its own useCallback() Hook.`,3558 ],3559 },3560 {3561 // Even if the function only references static values,3562 // once you specify it in deps, it will invalidate them.3563 // However, we can't suggest moving handleNext into the3564 // effect because it is *also* used outside of it.3565 // So our suggestion is useCallback().3566 code: `3567 function MyComponent(props) {3568 let [, setState] = useState();3569 const handleNext = (value) => {3570 setState(value);3571 };3572 useEffect(() => {3573 return Store.subscribe(handleNext);3574 }, [handleNext]);3575 return <div onClick={handleNext} />;3576 }3577 `,3578 // We autofix this one with useCallback since it's3579 // the easy fix and you can't just move it into effect.3580 output: `3581 function MyComponent(props) {3582 let [, setState] = useState();3583 const handleNext = useCallback((value) => {3584 setState(value);3585 });3586 useEffect(() => {3587 return Store.subscribe(handleNext);3588 }, [handleNext]);3589 return <div onClick={handleNext} />;3590 }3591 `,3592 errors: [3593 `The 'handleNext' function makes the dependencies of ` +3594 `useEffect Hook (at line 11) change on every render. ` +3595 `To fix this, wrap the 'handleNext' definition into its own useCallback() Hook.`,3596 ],3597 },3598 {3599 code: `3600 function MyComponent(props) {3601 function handleNext1() {3602 console.log('hello');3603 }3604 const handleNext2 = () => {3605 console.log('hello');3606 };3607 let handleNext3 = function() {3608 console.log('hello');3609 };3610 useEffect(() => {3611 return Store.subscribe(handleNext1);3612 }, [handleNext1]);3613 useLayoutEffect(() => {3614 return Store.subscribe(handleNext2);3615 }, [handleNext2]);3616 useMemo(() => {3617 return Store.subscribe(handleNext3);3618 }, [handleNext3]);3619 }3620 `,3621 // Autofix doesn't wrap into useCallback here3622 // because they are only referenced by effect itself.3623 output: `3624 function MyComponent(props) {3625 function handleNext1() {3626 console.log('hello');3627 }3628 const handleNext2 = () => {3629 console.log('hello');3630 };3631 let handleNext3 = function() {3632 console.log('hello');3633 };3634 useEffect(() => {3635 return Store.subscribe(handleNext1);3636 }, [handleNext1]);3637 useLayoutEffect(() => {3638 return Store.subscribe(handleNext2);3639 }, [handleNext2]);3640 useMemo(() => {3641 return Store.subscribe(handleNext3);3642 }, [handleNext3]);3643 }3644 `,3645 errors: [3646 "The 'handleNext1' function makes the dependencies of useEffect Hook " +3647 '(at line 14) change on every render. Move it inside the useEffect callback. ' +3648 "Alternatively, wrap the 'handleNext1' definition into its own useCallback() Hook.",3649 "The 'handleNext2' function makes the dependencies of useLayoutEffect Hook " +3650 '(at line 17) change on every render. Move it inside the useLayoutEffect callback. ' +3651 "Alternatively, wrap the 'handleNext2' definition into its own useCallback() Hook.",3652 "The 'handleNext3' function makes the dependencies of useMemo Hook " +3653 '(at line 20) change on every render. Move it inside the useMemo callback. ' +3654 "Alternatively, wrap the 'handleNext3' definition into its own useCallback() Hook.",3655 ],3656 },3657 {3658 code: `3659 function MyComponent(props) {3660 function handleNext1() {3661 console.log('hello');3662 }3663 const handleNext2 = () => {3664 console.log('hello');3665 };3666 let handleNext3 = function() {3667 console.log('hello');3668 };3669 useEffect(() => {3670 handleNext1();3671 return Store.subscribe(() => handleNext1());3672 }, [handleNext1]);3673 useLayoutEffect(() => {3674 handleNext2();3675 return Store.subscribe(() => handleNext2());3676 }, [handleNext2]);3677 useMemo(() => {3678 handleNext3();3679 return Store.subscribe(() => handleNext3());3680 }, [handleNext3]);3681 }3682 `,3683 // Autofix doesn't wrap into useCallback here3684 // because they are only referenced by effect itself.3685 output: `3686 function MyComponent(props) {3687 function handleNext1() {3688 console.log('hello');3689 }3690 const handleNext2 = () => {3691 console.log('hello');3692 };3693 let handleNext3 = function() {3694 console.log('hello');3695 };3696 useEffect(() => {3697 handleNext1();3698 return Store.subscribe(() => handleNext1());3699 }, [handleNext1]);3700 useLayoutEffect(() => {3701 handleNext2();3702 return Store.subscribe(() => handleNext2());3703 }, [handleNext2]);3704 useMemo(() => {3705 handleNext3();3706 return Store.subscribe(() => handleNext3());3707 }, [handleNext3]);3708 }3709 `,3710 errors: [3711 "The 'handleNext1' function makes the dependencies of useEffect Hook " +3712 '(at line 15) change on every render. Move it inside the useEffect callback. ' +3713 "Alternatively, wrap the 'handleNext1' definition into its own useCallback() Hook.",3714 "The 'handleNext2' function makes the dependencies of useLayoutEffect Hook " +3715 '(at line 19) change on every render. Move it inside the useLayoutEffect callback. ' +3716 "Alternatively, wrap the 'handleNext2' definition into its own useCallback() Hook.",3717 "The 'handleNext3' function makes the dependencies of useMemo Hook " +3718 '(at line 23) change on every render. Move it inside the useMemo callback. ' +3719 "Alternatively, wrap the 'handleNext3' definition into its own useCallback() Hook.",3720 ],3721 },3722 {3723 code: `3724 function MyComponent(props) {3725 function handleNext1() {3726 console.log('hello');3727 }3728 const handleNext2 = () => {3729 console.log('hello');3730 };3731 let handleNext3 = function() {3732 console.log('hello');3733 };3734 useEffect(() => {3735 handleNext1();3736 return Store.subscribe(() => handleNext1());3737 }, [handleNext1]);3738 useLayoutEffect(() => {3739 handleNext2();3740 return Store.subscribe(() => handleNext2());3741 }, [handleNext2]);3742 useMemo(() => {3743 handleNext3();3744 return Store.subscribe(() => handleNext3());3745 }, [handleNext3]);3746 return (3747 <div3748 onClick={() => {3749 handleNext1();3750 setTimeout(handleNext2);3751 setTimeout(() => {3752 handleNext3();3753 });3754 }}3755 />3756 );3757 }3758 `,3759 // Autofix wraps into useCallback where possible (variables only)3760 // because they are only referenced outside the effect.3761 output: `3762 function MyComponent(props) {3763 function handleNext1() {3764 console.log('hello');3765 }3766 const handleNext2 = useCallback(() => {3767 console.log('hello');3768 });3769 let handleNext3 = useCallback(function() {3770 console.log('hello');3771 });3772 useEffect(() => {3773 handleNext1();3774 return Store.subscribe(() => handleNext1());3775 }, [handleNext1]);3776 useLayoutEffect(() => {3777 handleNext2();3778 return Store.subscribe(() => handleNext2());3779 }, [handleNext2]);3780 useMemo(() => {3781 handleNext3();3782 return Store.subscribe(() => handleNext3());3783 }, [handleNext3]);3784 return (3785 <div3786 onClick={() => {3787 handleNext1();3788 setTimeout(handleNext2);3789 setTimeout(() => {3790 handleNext3();3791 });3792 }}3793 />3794 );3795 }3796 `,3797 errors: [3798 "The 'handleNext1' function makes the dependencies of useEffect Hook " +3799 '(at line 15) change on every render. To fix this, wrap the ' +3800 "'handleNext1' definition into its own useCallback() Hook.",3801 "The 'handleNext2' function makes the dependencies of useLayoutEffect Hook " +3802 '(at line 19) change on every render. To fix this, wrap the ' +3803 "'handleNext2' definition into its own useCallback() Hook.",3804 "The 'handleNext3' function makes the dependencies of useMemo Hook " +3805 '(at line 23) change on every render. To fix this, wrap the ' +3806 "'handleNext3' definition into its own useCallback() Hook.",3807 ],3808 },3809 {3810 code: `3811 function MyComponent(props) {3812 const handleNext1 = () => {3813 console.log('hello');3814 };3815 function handleNext2() {3816 console.log('hello');3817 }3818 useEffect(() => {3819 return Store.subscribe(handleNext1);3820 return Store.subscribe(handleNext2);3821 }, [handleNext1, handleNext2]);3822 useEffect(() => {3823 return Store.subscribe(handleNext1);3824 return Store.subscribe(handleNext2);3825 }, [handleNext1, handleNext2]);3826 }3827 `,3828 // Normally we'd suggest moving handleNext inside an3829 // effect. But it's used by more than one. So we3830 // suggest useCallback() and use it for the autofix3831 // where possible (variable but not declaration).3832 output: `3833 function MyComponent(props) {3834 const handleNext1 = useCallback(() => {3835 console.log('hello');3836 });3837 function handleNext2() {3838 console.log('hello');3839 }3840 useEffect(() => {3841 return Store.subscribe(handleNext1);3842 return Store.subscribe(handleNext2);3843 }, [handleNext1, handleNext2]);3844 useEffect(() => {3845 return Store.subscribe(handleNext1);3846 return Store.subscribe(handleNext2);3847 }, [handleNext1, handleNext2]);3848 }3849 `,3850 // TODO: we could coalesce messages for the same function if it affects multiple Hooks.3851 errors: [3852 "The 'handleNext1' function makes the dependencies of useEffect Hook " +3853 '(at line 12) change on every render. To fix this, wrap the ' +3854 "'handleNext1' definition into its own useCallback() Hook.",3855 "The 'handleNext1' function makes the dependencies of useEffect Hook " +3856 '(at line 16) change on every render. To fix this, wrap the ' +3857 "'handleNext1' definition into its own useCallback() Hook.",3858 "The 'handleNext2' function makes the dependencies of useEffect Hook " +3859 '(at line 12) change on every render. To fix this, wrap the ' +3860 "'handleNext2' definition into its own useCallback() Hook.",3861 "The 'handleNext2' function makes the dependencies of useEffect Hook " +3862 '(at line 16) change on every render. To fix this, wrap the ' +3863 "'handleNext2' definition into its own useCallback() Hook.",3864 ],3865 },3866 {3867 code: `3868 function MyComponent(props) {3869 let handleNext = () => {3870 console.log('hello');3871 };3872 if (props.foo) {3873 handleNext = () => {3874 console.log('hello');3875 };3876 }3877 useEffect(() => {3878 return Store.subscribe(handleNext);3879 }, [handleNext]);3880 }3881 `,3882 // Normally we'd suggest moving handleNext inside an3883 // effect. But it's used more than once.3884 // TODO: our autofix here isn't quite sufficient because3885 // it only wraps the first definition. But seems ok.3886 output: `3887 function MyComponent(props) {3888 let handleNext = useCallback(() => {3889 console.log('hello');3890 });3891 if (props.foo) {3892 handleNext = () => {3893 console.log('hello');3894 };3895 }3896 useEffect(() => {3897 return Store.subscribe(handleNext);3898 }, [handleNext]);3899 }3900 `,3901 errors: [3902 "The 'handleNext' function makes the dependencies of useEffect Hook " +3903 '(at line 13) change on every render. To fix this, wrap the ' +3904 "'handleNext' definition into its own useCallback() Hook.",3905 ],3906 },3907 {3908 code: `3909 function MyComponent(props) {3910 let [, setState] = useState();3911 let taint = props.foo;3912 function handleNext(value) {3913 let value2 = value * taint;3914 setState(value2);3915 console.log('hello');3916 }3917 useEffect(() => {3918 return Store.subscribe(handleNext);3919 }, [handleNext]);3920 }3921 `,3922 output: `3923 function MyComponent(props) {3924 let [, setState] = useState();3925 let taint = props.foo;3926 function handleNext(value) {3927 let value2 = value * taint;3928 setState(value2);3929 console.log('hello');3930 }3931 useEffect(() => {3932 return Store.subscribe(handleNext);3933 }, [handleNext]);3934 }3935 `,3936 errors: [3937 `The 'handleNext' function makes the dependencies of ` +3938 `useEffect Hook (at line 14) change on every render. ` +3939 `Move it inside the useEffect callback. Alternatively, wrap the ` +3940 `'handleNext' definition into its own useCallback() Hook.`,3941 ],3942 },3943 {3944 code: `3945 function Counter() {3946 let [count, setCount] = useState(0);3947 useEffect(() => {3948 let id = setInterval(() => {3949 setCount(count + 1);3950 }, 1000);3951 return () => clearInterval(id);3952 }, []);3953 return <h1>{count}</h1>;3954 }3955 `,3956 output: `3957 function Counter() {3958 let [count, setCount] = useState(0);3959 useEffect(() => {3960 let id = setInterval(() => {3961 setCount(count + 1);3962 }, 1000);3963 return () => clearInterval(id);3964 }, [count]);3965 return <h1>{count}</h1>;3966 }3967 `,3968 errors: [3969 "React Hook useEffect has a missing dependency: 'count'. " +3970 'Either include it or remove the dependency array. ' +3971 `You can also do a functional update 'setCount(c => ...)' if you ` +3972 `only need 'count' in the 'setCount' call.`,3973 ],3974 },3975 {3976 code: `3977 function Counter() {3978 let [count, setCount] = useState(0);3979 let [increment, setIncrement] = useState(0);3980 useEffect(() => {3981 let id = setInterval(() => {3982 setCount(count + increment);3983 }, 1000);3984 return () => clearInterval(id);3985 }, []);3986 return <h1>{count}</h1>;3987 }3988 `,3989 output: `3990 function Counter() {3991 let [count, setCount] = useState(0);3992 let [increment, setIncrement] = useState(0);3993 useEffect(() => {3994 let id = setInterval(() => {3995 setCount(count + increment);3996 }, 1000);3997 return () => clearInterval(id);3998 }, [count, increment]);3999 return <h1>{count}</h1>;4000 }4001 `,4002 errors: [4003 "React Hook useEffect has missing dependencies: 'count' and 'increment'. " +4004 'Either include them or remove the dependency array. ' +4005 `You can also do a functional update 'setCount(c => ...)' if you ` +4006 `only need 'count' in the 'setCount' call.`,4007 ],4008 },4009 {4010 code: `4011 function Counter() {4012 let [count, setCount] = useState(0);4013 let [increment, setIncrement] = useState(0);4014 useEffect(() => {4015 let id = setInterval(() => {4016 setCount(count => count + increment);4017 }, 1000);4018 return () => clearInterval(id);4019 }, []);4020 return <h1>{count}</h1>;4021 }4022 `,4023 output: `4024 function Counter() {4025 let [count, setCount] = useState(0);4026 let [increment, setIncrement] = useState(0);4027 useEffect(() => {4028 let id = setInterval(() => {4029 setCount(count => count + increment);4030 }, 1000);4031 return () => clearInterval(id);4032 }, [increment]);4033 return <h1>{count}</h1>;4034 }4035 `,4036 errors: [4037 "React Hook useEffect has a missing dependency: 'increment'. " +4038 'Either include it or remove the dependency array. ' +4039 `You can also replace multiple useState variables with useReducer ` +4040 `if 'setCount' needs the current value of 'increment'.`,4041 ],4042 },4043 {4044 code: `4045 function Counter() {4046 let [count, setCount] = useState(0);4047 let increment = useCustomHook();4048 useEffect(() => {4049 let id = setInterval(() => {4050 setCount(count => count + increment);4051 }, 1000);4052 return () => clearInterval(id);4053 }, []);4054 return <h1>{count}</h1>;4055 }4056 `,4057 output: `4058 function Counter() {4059 let [count, setCount] = useState(0);4060 let increment = useCustomHook();4061 useEffect(() => {4062 let id = setInterval(() => {4063 setCount(count => count + increment);4064 }, 1000);4065 return () => clearInterval(id);4066 }, [increment]);4067 return <h1>{count}</h1>;4068 }4069 `,4070 // This intentionally doesn't show the reducer message4071 // because we don't know if it's safe for it to close over a value.4072 // We only show it for state variables (and possibly props).4073 errors: [4074 "React Hook useEffect has a missing dependency: 'increment'. " +4075 'Either include it or remove the dependency array.',4076 ],4077 },4078 {4079 code: `4080 function Counter({ step }) {4081 let [count, setCount] = useState(0);4082 function increment(x) {4083 return x + step;4084 }4085 useEffect(() => {4086 let id = setInterval(() => {4087 setCount(count => increment(count));4088 }, 1000);4089 return () => clearInterval(id);4090 }, []);4091 return <h1>{count}</h1>;4092 }4093 `,4094 output: `4095 function Counter({ step }) {4096 let [count, setCount] = useState(0);4097 function increment(x) {4098 return x + step;4099 }4100 useEffect(() => {4101 let id = setInterval(() => {4102 setCount(count => increment(count));4103 }, 1000);4104 return () => clearInterval(id);4105 }, [increment]);4106 return <h1>{count}</h1>;4107 }4108 `,4109 // This intentionally doesn't show the reducer message4110 // because we don't know if it's safe for it to close over a value.4111 // We only show it for state variables (and possibly props).4112 errors: [4113 "React Hook useEffect has a missing dependency: 'increment'. " +4114 'Either include it or remove the dependency array.',4115 ],4116 },4117 {4118 code: `4119 function Counter({ step }) {4120 let [count, setCount] = useState(0);4121 function increment(x) {4122 return x + step;4123 }4124 useEffect(() => {4125 let id = setInterval(() => {4126 setCount(count => increment(count));4127 }, 1000);4128 return () => clearInterval(id);4129 }, [increment]);4130 return <h1>{count}</h1>;4131 }4132 `,4133 output: `4134 function Counter({ step }) {4135 let [count, setCount] = useState(0);4136 function increment(x) {4137 return x + step;4138 }4139 useEffect(() => {4140 let id = setInterval(() => {4141 setCount(count => increment(count));4142 }, 1000);4143 return () => clearInterval(id);4144 }, [increment]);4145 return <h1>{count}</h1>;4146 }4147 `,4148 errors: [4149 `The 'increment' function makes the dependencies of useEffect Hook ` +4150 `(at line 14) change on every render. Move it inside the useEffect callback. ` +4151 `Alternatively, wrap the \'increment\' definition into its own ` +4152 `useCallback() Hook.`,4153 ],4154 },4155 {4156 code: `4157 function Counter({ increment }) {4158 let [count, setCount] = useState(0);4159 useEffect(() => {4160 let id = setInterval(() => {4161 setCount(count => count + increment);4162 }, 1000);4163 return () => clearInterval(id);4164 }, []);4165 return <h1>{count}</h1>;4166 }4167 `,4168 output: `4169 function Counter({ increment }) {4170 let [count, setCount] = useState(0);4171 useEffect(() => {4172 let id = setInterval(() => {4173 setCount(count => count + increment);4174 }, 1000);4175 return () => clearInterval(id);4176 }, [increment]);4177 return <h1>{count}</h1>;4178 }4179 `,4180 errors: [4181 "React Hook useEffect has a missing dependency: 'increment'. " +4182 'Either include it or remove the dependency array. ' +4183 `If 'setCount' needs the current value of 'increment', ` +4184 `you can also switch to useReducer instead of useState and read 'increment' in the reducer.`,4185 ],4186 },4187 {4188 code: `4189 function Counter() {4190 const [count, setCount] = useState(0);4191 function tick() {4192 setCount(count + 1);4193 }4194 useEffect(() => {4195 let id = setInterval(() => {4196 tick();4197 }, 1000);4198 return () => clearInterval(id);4199 }, []);4200 return <h1>{count}</h1>;4201 }4202 `,4203 output: `4204 function Counter() {4205 const [count, setCount] = useState(0);4206 function tick() {4207 setCount(count + 1);4208 }4209 useEffect(() => {4210 let id = setInterval(() => {4211 tick();4212 }, 1000);4213 return () => clearInterval(id);4214 }, [tick]);4215 return <h1>{count}</h1>;4216 }4217 `,4218 // TODO: ideally this should suggest useState updater form4219 // since this code doesn't actually work. The autofix could4220 // at least avoid suggesting 'tick' since it's obviously4221 // always different, and thus useless.4222 errors: [4223 "React Hook useEffect has a missing dependency: 'tick'. " +4224 'Either include it or remove the dependency array.',4225 ],4226 },4227 {4228 // Regression test for a crash4229 code: `4230 function Podcasts() {4231 useEffect(() => {4232 alert(podcasts);4233 }, []);4234 let [podcasts, setPodcasts] = useState(null);4235 }4236 `,4237 // Note: this autofix is shady because4238 // the variable is used before declaration.4239 // TODO: Maybe we can catch those fixes and not autofix.4240 output: `4241 function Podcasts() {4242 useEffect(() => {4243 alert(podcasts);4244 }, [podcasts]);4245 let [podcasts, setPodcasts] = useState(null);4246 }4247 `,4248 errors: [4249 `React Hook useEffect has a missing dependency: 'podcasts'. ` +4250 `Either include it or remove the dependency array.`,4251 ],4252 },4253 {4254 code: `4255 function Podcasts({ fetchPodcasts, id }) {4256 let [podcasts, setPodcasts] = useState(null);4257 useEffect(() => {4258 fetchPodcasts(id).then(setPodcasts);4259 }, [id]);4260 }4261 `,4262 output: `4263 function Podcasts({ fetchPodcasts, id }) {4264 let [podcasts, setPodcasts] = useState(null);4265 useEffect(() => {4266 fetchPodcasts(id).then(setPodcasts);4267 }, [fetchPodcasts, id]);4268 }4269 `,4270 errors: [4271 `React Hook useEffect has a missing dependency: 'fetchPodcasts'. ` +4272 `Either include it or remove the dependency array. ` +4273 `If 'fetchPodcasts' changes too often, ` +4274 `find the parent component that defines it and wrap that definition in useCallback.`,4275 ],4276 },4277 {4278 code: `4279 function Podcasts({ api: { fetchPodcasts }, id }) {4280 let [podcasts, setPodcasts] = useState(null);4281 useEffect(() => {4282 fetchPodcasts(id).then(setPodcasts);4283 }, [id]);4284 }4285 `,4286 output: `4287 function Podcasts({ api: { fetchPodcasts }, id }) {4288 let [podcasts, setPodcasts] = useState(null);4289 useEffect(() => {4290 fetchPodcasts(id).then(setPodcasts);4291 }, [fetchPodcasts, id]);4292 }4293 `,4294 errors: [4295 `React Hook useEffect has a missing dependency: 'fetchPodcasts'. ` +4296 `Either include it or remove the dependency array. ` +4297 `If 'fetchPodcasts' changes too often, ` +4298 `find the parent component that defines it and wrap that definition in useCallback.`,4299 ],4300 },4301 {4302 code: `4303 function Podcasts({ fetchPodcasts, fetchPodcasts2, id }) {4304 let [podcasts, setPodcasts] = useState(null);4305 useEffect(() => {4306 setTimeout(() => {4307 console.log(id);4308 fetchPodcasts(id).then(setPodcasts);4309 fetchPodcasts2(id).then(setPodcasts);4310 });4311 }, [id]);4312 }4313 `,4314 output: `4315 function Podcasts({ fetchPodcasts, fetchPodcasts2, id }) {4316 let [podcasts, setPodcasts] = useState(null);4317 useEffect(() => {4318 setTimeout(() => {4319 console.log(id);4320 fetchPodcasts(id).then(setPodcasts);4321 fetchPodcasts2(id).then(setPodcasts);4322 });4323 }, [fetchPodcasts, fetchPodcasts2, id]);4324 }4325 `,4326 errors: [4327 `React Hook useEffect has missing dependencies: 'fetchPodcasts' and 'fetchPodcasts2'. ` +4328 `Either include them or remove the dependency array. ` +4329 `If 'fetchPodcasts' changes too often, ` +4330 `find the parent component that defines it and wrap that definition in useCallback.`,4331 ],4332 },4333 {4334 code: `4335 function Podcasts({ fetchPodcasts, id }) {4336 let [podcasts, setPodcasts] = useState(null);4337 useEffect(() => {4338 console.log(fetchPodcasts);4339 fetchPodcasts(id).then(setPodcasts);4340 }, [id]);4341 }4342 `,4343 output: `4344 function Podcasts({ fetchPodcasts, id }) {4345 let [podcasts, setPodcasts] = useState(null);4346 useEffect(() => {4347 console.log(fetchPodcasts);4348 fetchPodcasts(id).then(setPodcasts);4349 }, [fetchPodcasts, id]);4350 }4351 `,4352 errors: [4353 `React Hook useEffect has a missing dependency: 'fetchPodcasts'. ` +4354 `Either include it or remove the dependency array. ` +4355 `If 'fetchPodcasts' changes too often, ` +4356 `find the parent component that defines it and wrap that definition in useCallback.`,4357 ],4358 },4359 {4360 // The mistake here is that it was moved inside the effect4361 // so it can't be referenced in the deps array.4362 code: `4363 function Thing() {4364 useEffect(() => {4365 const fetchData = async () => {};4366 fetchData();4367 }, [fetchData]);4368 }4369 `,4370 output: `4371 function Thing() {4372 useEffect(() => {4373 const fetchData = async () => {};4374 fetchData();4375 }, []);4376 }4377 `,4378 errors: [4379 `React Hook useEffect has an unnecessary dependency: 'fetchData'. ` +4380 `Either exclude it or remove the dependency array.`,4381 ],4382 },4383 {4384 code: `4385 function Hello() {4386 const [state, setState] = useState(0);4387 useEffect(() => {4388 setState({});4389 });4390 }4391 `,4392 output: `4393 function Hello() {4394 const [state, setState] = useState(0);4395 useEffect(() => {4396 setState({});4397 }, []);4398 }4399 `,4400 errors: [4401 `React Hook useEffect contains a call to 'setState'. ` +4402 `Without a list of dependencies, this can lead to an infinite chain of updates. ` +4403 `To fix this, pass [] as a second argument to the useEffect Hook.`,4404 ],4405 },4406 {4407 code: `4408 function Hello() {4409 const [data, setData] = useState(0);4410 useEffect(() => {4411 fetchData.then(setData);4412 });4413 }4414 `,4415 output: `4416 function Hello() {4417 const [data, setData] = useState(0);4418 useEffect(() => {4419 fetchData.then(setData);4420 }, []);4421 }4422 `,4423 errors: [4424 `React Hook useEffect contains a call to 'setData'. ` +4425 `Without a list of dependencies, this can lead to an infinite chain of updates. ` +4426 `To fix this, pass [] as a second argument to the useEffect Hook.`,4427 ],4428 },4429 {4430 code: `4431 function Hello({ country }) {4432 const [data, setData] = useState(0);4433 useEffect(() => {4434 fetchData(country).then(setData);4435 });4436 }4437 `,4438 output: `4439 function Hello({ country }) {4440 const [data, setData] = useState(0);4441 useEffect(() => {4442 fetchData(country).then(setData);4443 }, [country]);4444 }4445 `,4446 errors: [4447 `React Hook useEffect contains a call to 'setData'. ` +4448 `Without a list of dependencies, this can lead to an infinite chain of updates. ` +4449 `To fix this, pass [country] as a second argument to the useEffect Hook.`,4450 ],4451 },4452 {4453 code: `4454 function Hello({ prop1, prop2 }) {4455 const [state, setState] = useState(0);4456 useEffect(() => {4457 if (prop1) {4458 setState(prop2);4459 }4460 });4461 }4462 `,4463 output: `4464 function Hello({ prop1, prop2 }) {4465 const [state, setState] = useState(0);4466 useEffect(() => {4467 if (prop1) {4468 setState(prop2);4469 }4470 }, [prop1, prop2]);4471 }4472 `,4473 errors: [4474 `React Hook useEffect contains a call to 'setState'. ` +4475 `Without a list of dependencies, this can lead to an infinite chain of updates. ` +4476 `To fix this, pass [prop1, prop2] as a second argument to the useEffect Hook.`,4477 ],4478 },4479 {4480 code: `4481 function Thing() {4482 useEffect(async () => {}, []);4483 }4484 `,4485 output: `4486 function Thing() {4487 useEffect(async () => {}, []);4488 }4489 `,4490 errors: [4491 `Effect callbacks are synchronous to prevent race conditions. ` +4492 `Put the async function inside:\n\n` +4493 'useEffect(() => {\n' +4494 ' async function fetchData() {\n' +4495 ' // You can await here\n' +4496 ' const response = await MyAPI.getData(someId);\n' +4497 ' // ...\n' +4498 ' }\n' +4499 ' fetchData();\n' +4500 `}, [someId]); // Or [] if effect doesn't need props or state\n\n` +4501 'Learn more about data fetching with Hooks: https://fb.me/react-hooks-data-fetching',4502 ],4503 },4504 {4505 code: `4506 function Thing() {4507 useEffect(async () => {});4508 }4509 `,4510 output: `4511 function Thing() {4512 useEffect(async () => {});4513 }4514 `,4515 errors: [4516 `Effect callbacks are synchronous to prevent race conditions. ` +4517 `Put the async function inside:\n\n` +4518 'useEffect(() => {\n' +4519 ' async function fetchData() {\n' +4520 ' // You can await here\n' +4521 ' const response = await MyAPI.getData(someId);\n' +4522 ' // ...\n' +4523 ' }\n' +4524 ' fetchData();\n' +4525 `}, [someId]); // Or [] if effect doesn't need props or state\n\n` +4526 'Learn more about data fetching with Hooks: https://fb.me/react-hooks-data-fetching',4527 ],4528 },4529 {4530 code: `4531 function Example() {4532 const foo = useCallback(() => {...
StrictEffectsModeDefaults-test.internal.js
Source:StrictEffectsModeDefaults-test.internal.js
...23 ReactFeatureFlags.createRootStrictEffectsByDefault = __DEV__;24 });25 it('should not double invoke effects in legacy mode', () => {26 function App({text}) {27 React.useEffect(() => {28 Scheduler.unstable_yieldValue('useEffect mount');29 return () => Scheduler.unstable_yieldValue('useEffect unmount');30 });31 React.useLayoutEffect(() => {32 Scheduler.unstable_yieldValue('useLayoutEffect mount');33 return () => Scheduler.unstable_yieldValue('useLayoutEffect unmount');34 });35 return text;36 }37 act(() => {38 ReactNoop.renderLegacySyncRoot(<App text={'mount'} />);39 });40 expect(Scheduler).toHaveYielded([41 'useLayoutEffect mount',42 'useEffect mount',43 ]);44 });45 it('should not double invoke class lifecycles in legacy mode', () => {46 class App extends React.PureComponent {47 componentDidMount() {48 Scheduler.unstable_yieldValue('componentDidMount');49 }50 componentDidUpdate() {51 Scheduler.unstable_yieldValue('componentDidUpdate');52 }53 componentWillUnmount() {54 Scheduler.unstable_yieldValue('componentWillUnmount');55 }56 render() {57 return this.props.text;58 }59 }60 act(() => {61 ReactNoop.renderLegacySyncRoot(<App text={'mount'} />);62 });63 expect(Scheduler).toHaveYielded(['componentDidMount']);64 });65 if (__DEV__) {66 it('should flush double-invoked effects within the same frame as layout effects if there are no passive effects', () => {67 function ComponentWithEffects({label}) {68 React.useLayoutEffect(() => {69 Scheduler.unstable_yieldValue(`useLayoutEffect mount "${label}"`);70 return () =>71 Scheduler.unstable_yieldValue(`useLayoutEffect unmount "${label}"`);72 });73 return label;74 }75 act(() => {76 ReactNoop.render(77 <>78 <ComponentWithEffects label={'one'} />79 </>,80 );81 expect(Scheduler).toFlushUntilNextPaint([82 'useLayoutEffect mount "one"',83 'useLayoutEffect unmount "one"',84 'useLayoutEffect mount "one"',85 ]);86 });87 act(() => {88 ReactNoop.render(89 <>90 <ComponentWithEffects label={'one'} />91 <ComponentWithEffects label={'two'} />92 </>,93 );94 expect(Scheduler).toHaveYielded([]);95 expect(Scheduler).toFlushUntilNextPaint([96 // Cleanup and re-run "one" (and "two") since there is no dependencies array.97 'useLayoutEffect unmount "one"',98 'useLayoutEffect mount "one"',99 'useLayoutEffect mount "two"',100 // Since "two" is new, it should be double-invoked.101 'useLayoutEffect unmount "two"',102 'useLayoutEffect mount "two"',103 ]);104 });105 });106 // This test also verifies that double-invoked effects flush synchronously107 // within the same frame as passive effects.108 it('should double invoke effects only for newly mounted components', () => {109 function ComponentWithEffects({label}) {110 React.useEffect(() => {111 Scheduler.unstable_yieldValue(`useEffect mount "${label}"`);112 return () =>113 Scheduler.unstable_yieldValue(`useEffect unmount "${label}"`);114 });115 React.useLayoutEffect(() => {116 Scheduler.unstable_yieldValue(`useLayoutEffect mount "${label}"`);117 return () =>118 Scheduler.unstable_yieldValue(`useLayoutEffect unmount "${label}"`);119 });120 return label;121 }122 act(() => {123 ReactNoop.render(124 <>125 <ComponentWithEffects label={'one'} />126 </>,127 );128 expect(Scheduler).toFlushAndYieldThrough([129 'useLayoutEffect mount "one"',130 ]);131 expect(Scheduler).toFlushAndYield([132 'useEffect mount "one"',133 'useLayoutEffect unmount "one"',134 'useEffect unmount "one"',135 'useLayoutEffect mount "one"',136 'useEffect mount "one"',137 ]);138 });139 act(() => {140 ReactNoop.render(141 <>142 <ComponentWithEffects label={'one'} />143 <ComponentWithEffects label={'two'} />144 </>,145 );146 expect(Scheduler).toFlushAndYieldThrough([147 // Cleanup and re-run "one" (and "two") since there is no dependencies array.148 'useLayoutEffect unmount "one"',149 'useLayoutEffect mount "one"',150 'useLayoutEffect mount "two"',151 ]);152 expect(Scheduler).toFlushAndYield([153 'useEffect unmount "one"',154 'useEffect mount "one"',155 'useEffect mount "two"',156 // Since "two" is new, it should be double-invoked.157 'useLayoutEffect unmount "two"',158 'useEffect unmount "two"',159 'useLayoutEffect mount "two"',160 'useEffect mount "two"',161 ]);162 });163 });164 it('double invoking for effects for modern roots', () => {165 function App({text}) {166 React.useEffect(() => {167 Scheduler.unstable_yieldValue('useEffect mount');168 return () => Scheduler.unstable_yieldValue('useEffect unmount');169 });170 React.useLayoutEffect(() => {171 Scheduler.unstable_yieldValue('useLayoutEffect mount');172 return () => Scheduler.unstable_yieldValue('useLayoutEffect unmount');173 });174 return text;175 }176 act(() => {177 ReactNoop.render(<App text={'mount'} />);178 });179 expect(Scheduler).toHaveYielded([180 'useLayoutEffect mount',181 'useEffect mount',182 'useLayoutEffect unmount',183 'useEffect unmount',184 'useLayoutEffect mount',185 'useEffect mount',186 ]);187 act(() => {188 ReactNoop.render(<App text={'update'} />);189 });190 expect(Scheduler).toHaveYielded([191 'useLayoutEffect unmount',192 'useLayoutEffect mount',193 'useEffect unmount',194 'useEffect mount',195 ]);196 act(() => {197 ReactNoop.render(null);198 });199 expect(Scheduler).toHaveYielded([200 'useLayoutEffect unmount',201 'useEffect unmount',202 ]);203 });204 it('multiple effects are double invoked in the right order (all mounted, all unmounted, all remounted)', () => {205 function App({text}) {206 React.useEffect(() => {207 Scheduler.unstable_yieldValue('useEffect One mount');208 return () => Scheduler.unstable_yieldValue('useEffect One unmount');209 });210 React.useEffect(() => {211 Scheduler.unstable_yieldValue('useEffect Two mount');212 return () => Scheduler.unstable_yieldValue('useEffect Two unmount');213 });214 return text;215 }216 act(() => {217 ReactNoop.render(<App text={'mount'} />);218 });219 expect(Scheduler).toHaveYielded([220 'useEffect One mount',221 'useEffect Two mount',222 'useEffect One unmount',223 'useEffect Two unmount',224 'useEffect One mount',225 'useEffect Two mount',226 ]);227 act(() => {228 ReactNoop.render(<App text={'update'} />);229 });230 expect(Scheduler).toHaveYielded([231 'useEffect One unmount',232 'useEffect Two unmount',233 'useEffect One mount',234 'useEffect Two mount',235 ]);236 act(() => {237 ReactNoop.render(null);238 });239 expect(Scheduler).toHaveYielded([240 'useEffect One unmount',241 'useEffect Two unmount',242 ]);243 });244 it('multiple layout effects are double invoked in the right order (all mounted, all unmounted, all remounted)', () => {245 function App({text}) {246 React.useLayoutEffect(() => {247 Scheduler.unstable_yieldValue('useLayoutEffect One mount');248 return () =>249 Scheduler.unstable_yieldValue('useLayoutEffect One unmount');250 });251 React.useLayoutEffect(() => {252 Scheduler.unstable_yieldValue('useLayoutEffect Two mount');253 return () =>254 Scheduler.unstable_yieldValue('useLayoutEffect Two unmount');255 });256 return text;257 }258 act(() => {259 ReactNoop.render(<App text={'mount'} />);260 });261 expect(Scheduler).toHaveYielded([262 'useLayoutEffect One mount',263 'useLayoutEffect Two mount',264 'useLayoutEffect One unmount',265 'useLayoutEffect Two unmount',266 'useLayoutEffect One mount',267 'useLayoutEffect Two mount',268 ]);269 act(() => {270 ReactNoop.render(<App text={'update'} />);271 });272 expect(Scheduler).toHaveYielded([273 'useLayoutEffect One unmount',274 'useLayoutEffect Two unmount',275 'useLayoutEffect One mount',276 'useLayoutEffect Two mount',277 ]);278 act(() => {279 ReactNoop.render(null);280 });281 expect(Scheduler).toHaveYielded([282 'useLayoutEffect One unmount',283 'useLayoutEffect Two unmount',284 ]);285 });286 it('useEffect and useLayoutEffect is called twice when there is no unmount', () => {287 function App({text}) {288 React.useEffect(() => {289 Scheduler.unstable_yieldValue('useEffect mount');290 });291 React.useLayoutEffect(() => {292 Scheduler.unstable_yieldValue('useLayoutEffect mount');293 });294 return text;295 }296 act(() => {297 ReactNoop.render(<App text={'mount'} />);298 });299 expect(Scheduler).toHaveYielded([300 'useLayoutEffect mount',301 'useEffect mount',302 'useLayoutEffect mount',303 'useEffect mount',304 ]);305 act(() => {306 ReactNoop.render(<App text={'update'} />);307 });308 expect(Scheduler).toHaveYielded([309 'useLayoutEffect mount',310 'useEffect mount',311 ]);312 act(() => {313 ReactNoop.render(null);314 });315 expect(Scheduler).toHaveYielded([]);316 });317 it('passes the right context to class component lifecycles', () => {318 class App extends React.PureComponent {319 test() {}320 componentDidMount() {321 this.test();322 Scheduler.unstable_yieldValue('componentDidMount');323 }324 componentDidUpdate() {325 this.test();326 Scheduler.unstable_yieldValue('componentDidUpdate');327 }328 componentWillUnmount() {329 this.test();330 Scheduler.unstable_yieldValue('componentWillUnmount');331 }332 render() {333 return null;334 }335 }336 act(() => {337 ReactNoop.render(<App />);338 });339 expect(Scheduler).toHaveYielded([340 'componentDidMount',341 'componentWillUnmount',342 'componentDidMount',343 ]);344 });345 it('double invoking works for class components', () => {346 class App extends React.PureComponent {347 componentDidMount() {348 Scheduler.unstable_yieldValue('componentDidMount');349 }350 componentDidUpdate() {351 Scheduler.unstable_yieldValue('componentDidUpdate');352 }353 componentWillUnmount() {354 Scheduler.unstable_yieldValue('componentWillUnmount');355 }356 render() {357 return this.props.text;358 }359 }360 act(() => {361 ReactNoop.render(<App text={'mount'} />);362 });363 expect(Scheduler).toHaveYielded([364 'componentDidMount',365 'componentWillUnmount',366 'componentDidMount',367 ]);368 act(() => {369 ReactNoop.render(<App text={'update'} />);370 });371 expect(Scheduler).toHaveYielded(['componentDidUpdate']);372 act(() => {373 ReactNoop.render(null);374 });375 expect(Scheduler).toHaveYielded(['componentWillUnmount']);376 });377 it('double flushing passive effects only results in one double invoke', () => {378 function App({text}) {379 const [state, setState] = React.useState(0);380 React.useEffect(() => {381 if (state !== 1) {382 setState(1);383 }384 Scheduler.unstable_yieldValue('useEffect mount');385 return () => Scheduler.unstable_yieldValue('useEffect unmount');386 });387 React.useLayoutEffect(() => {388 Scheduler.unstable_yieldValue('useLayoutEffect mount');389 return () => Scheduler.unstable_yieldValue('useLayoutEffect unmount');390 });391 Scheduler.unstable_yieldValue(text);392 return text;393 }394 act(() => {395 ReactNoop.render(<App text={'mount'} />);396 });397 expect(Scheduler).toHaveYielded([398 'mount',399 'useLayoutEffect mount',400 'useEffect mount',401 'useLayoutEffect unmount',402 'useEffect unmount',403 'useLayoutEffect mount',404 'useEffect mount',405 'mount',406 'useLayoutEffect unmount',407 'useLayoutEffect mount',408 'useEffect unmount',409 'useEffect mount',410 ]);411 });412 it('newly mounted components after initial mount get double invoked', () => {413 let _setShowChild;414 function Child() {415 React.useEffect(() => {416 Scheduler.unstable_yieldValue('Child useEffect mount');417 return () => Scheduler.unstable_yieldValue('Child useEffect unmount');418 });419 React.useLayoutEffect(() => {420 Scheduler.unstable_yieldValue('Child useLayoutEffect mount');421 return () =>422 Scheduler.unstable_yieldValue('Child useLayoutEffect unmount');423 });424 return null;425 }426 function App() {427 const [showChild, setShowChild] = React.useState(false);428 _setShowChild = setShowChild;429 React.useEffect(() => {430 Scheduler.unstable_yieldValue('App useEffect mount');431 return () => Scheduler.unstable_yieldValue('App useEffect unmount');432 });433 React.useLayoutEffect(() => {434 Scheduler.unstable_yieldValue('App useLayoutEffect mount');435 return () =>436 Scheduler.unstable_yieldValue('App useLayoutEffect unmount');437 });438 return showChild && <Child />;439 }440 act(() => {441 ReactNoop.render(<App />);442 });443 expect(Scheduler).toHaveYielded([444 'App useLayoutEffect mount',445 'App useEffect mount',446 'App useLayoutEffect unmount',447 'App useEffect unmount',448 'App useLayoutEffect mount',449 'App useEffect mount',450 ]);451 act(() => {452 _setShowChild(true);453 });454 expect(Scheduler).toHaveYielded([455 'App useLayoutEffect unmount',456 'Child useLayoutEffect mount',457 'App useLayoutEffect mount',458 'App useEffect unmount',459 'Child useEffect mount',460 'App useEffect mount',461 'Child useLayoutEffect unmount',462 'Child useEffect unmount',463 'Child useLayoutEffect mount',464 'Child useEffect mount',465 ]);466 });467 it('classes and functions are double invoked together correctly', () => {468 class ClassChild extends React.PureComponent {469 componentDidMount() {470 Scheduler.unstable_yieldValue('componentDidMount');471 }472 componentWillUnmount() {473 Scheduler.unstable_yieldValue('componentWillUnmount');474 }475 render() {476 return this.props.text;477 }478 }479 function FunctionChild({text}) {480 React.useEffect(() => {481 Scheduler.unstable_yieldValue('useEffect mount');482 return () => Scheduler.unstable_yieldValue('useEffect unmount');483 });484 React.useLayoutEffect(() => {485 Scheduler.unstable_yieldValue('useLayoutEffect mount');486 return () => Scheduler.unstable_yieldValue('useLayoutEffect unmount');487 });488 return text;489 }490 function App({text}) {491 return (492 <>493 <ClassChild text={text} />494 <FunctionChild text={text} />...
StrictEffectsMode-test.js
Source:StrictEffectsMode-test.js
...29 );30 }31 it('should not double invoke effects in legacy mode', () => {32 function App({text}) {33 React.useEffect(() => {34 Scheduler.unstable_yieldValue('useEffect mount');35 return () => Scheduler.unstable_yieldValue('useEffect unmount');36 });37 React.useLayoutEffect(() => {38 Scheduler.unstable_yieldValue('useLayoutEffect mount');39 return () => Scheduler.unstable_yieldValue('useLayoutEffect unmount');40 });41 return text;42 }43 act(() => {44 ReactTestRenderer.create(<App text={'mount'} />);45 });46 expect(Scheduler).toHaveYielded([47 'useLayoutEffect mount',48 'useEffect mount',49 ]);50 });51 it('double invoking for effects works properly', () => {52 function App({text}) {53 React.useEffect(() => {54 Scheduler.unstable_yieldValue('useEffect mount');55 return () => Scheduler.unstable_yieldValue('useEffect unmount');56 });57 React.useLayoutEffect(() => {58 Scheduler.unstable_yieldValue('useLayoutEffect mount');59 return () => Scheduler.unstable_yieldValue('useLayoutEffect unmount');60 });61 return text;62 }63 let renderer;64 act(() => {65 renderer = ReactTestRenderer.create(<App text={'mount'} />, {66 unstable_isConcurrent: true,67 });68 });69 if (supportsDoubleInvokeEffects()) {70 expect(Scheduler).toHaveYielded([71 'useLayoutEffect mount',72 'useEffect mount',73 'useLayoutEffect unmount',74 'useEffect unmount',75 'useLayoutEffect mount',76 'useEffect mount',77 ]);78 } else {79 expect(Scheduler).toHaveYielded([80 'useLayoutEffect mount',81 'useEffect mount',82 ]);83 }84 act(() => {85 renderer.update(<App text={'update'} />);86 });87 expect(Scheduler).toHaveYielded([88 'useLayoutEffect unmount',89 'useLayoutEffect mount',90 'useEffect unmount',91 'useEffect mount',92 ]);93 act(() => {94 renderer.unmount();95 });96 expect(Scheduler).toHaveYielded([97 'useLayoutEffect unmount',98 'useEffect unmount',99 ]);100 });101 it('multiple effects are double invoked in the right order (all mounted, all unmounted, all remounted)', () => {102 function App({text}) {103 React.useEffect(() => {104 Scheduler.unstable_yieldValue('useEffect One mount');105 return () => Scheduler.unstable_yieldValue('useEffect One unmount');106 });107 React.useEffect(() => {108 Scheduler.unstable_yieldValue('useEffect Two mount');109 return () => Scheduler.unstable_yieldValue('useEffect Two unmount');110 });111 return text;112 }113 let renderer;114 act(() => {115 renderer = ReactTestRenderer.create(<App text={'mount'} />, {116 unstable_isConcurrent: true,117 });118 });119 if (supportsDoubleInvokeEffects()) {120 expect(Scheduler).toHaveYielded([121 'useEffect One mount',122 'useEffect Two mount',123 'useEffect One unmount',124 'useEffect Two unmount',125 'useEffect One mount',126 'useEffect Two mount',127 ]);128 } else {129 expect(Scheduler).toHaveYielded([130 'useEffect One mount',131 'useEffect Two mount',132 ]);133 }134 act(() => {135 renderer.update(<App text={'update'} />);136 });137 expect(Scheduler).toHaveYielded([138 'useEffect One unmount',139 'useEffect Two unmount',140 'useEffect One mount',141 'useEffect Two mount',142 ]);143 act(() => {144 renderer.unmount(null);145 });146 expect(Scheduler).toHaveYielded([147 'useEffect One unmount',148 'useEffect Two unmount',149 ]);150 });151 it('multiple layout effects are double invoked in the right order (all mounted, all unmounted, all remounted)', () => {152 function App({text}) {153 React.useLayoutEffect(() => {154 Scheduler.unstable_yieldValue('useLayoutEffect One mount');155 return () =>156 Scheduler.unstable_yieldValue('useLayoutEffect One unmount');157 });158 React.useLayoutEffect(() => {159 Scheduler.unstable_yieldValue('useLayoutEffect Two mount');160 return () =>161 Scheduler.unstable_yieldValue('useLayoutEffect Two unmount');162 });163 return text;164 }165 let renderer;166 act(() => {167 renderer = ReactTestRenderer.create(<App text={'mount'} />, {168 unstable_isConcurrent: true,169 });170 });171 if (supportsDoubleInvokeEffects()) {172 expect(Scheduler).toHaveYielded([173 'useLayoutEffect One mount',174 'useLayoutEffect Two mount',175 'useLayoutEffect One unmount',176 'useLayoutEffect Two unmount',177 'useLayoutEffect One mount',178 'useLayoutEffect Two mount',179 ]);180 } else {181 expect(Scheduler).toHaveYielded([182 'useLayoutEffect One mount',183 'useLayoutEffect Two mount',184 ]);185 }186 act(() => {187 renderer.update(<App text={'update'} />);188 });189 expect(Scheduler).toHaveYielded([190 'useLayoutEffect One unmount',191 'useLayoutEffect Two unmount',192 'useLayoutEffect One mount',193 'useLayoutEffect Two mount',194 ]);195 act(() => {196 renderer.unmount();197 });198 expect(Scheduler).toHaveYielded([199 'useLayoutEffect One unmount',200 'useLayoutEffect Two unmount',201 ]);202 });203 it('useEffect and useLayoutEffect is called twice when there is no unmount', () => {204 function App({text}) {205 React.useEffect(() => {206 Scheduler.unstable_yieldValue('useEffect mount');207 });208 React.useLayoutEffect(() => {209 Scheduler.unstable_yieldValue('useLayoutEffect mount');210 });211 return text;212 }213 let renderer;214 act(() => {215 renderer = ReactTestRenderer.create(<App text={'mount'} />, {216 unstable_isConcurrent: true,217 });218 });219 if (supportsDoubleInvokeEffects()) {220 expect(Scheduler).toHaveYielded([221 'useLayoutEffect mount',222 'useEffect mount',223 'useLayoutEffect mount',224 'useEffect mount',225 ]);226 } else {227 expect(Scheduler).toHaveYielded([228 'useLayoutEffect mount',229 'useEffect mount',230 ]);231 }232 act(() => {233 renderer.update(<App text={'update'} />);234 });235 expect(Scheduler).toHaveYielded([236 'useLayoutEffect mount',237 'useEffect mount',238 ]);239 act(() => {240 renderer.unmount();241 });242 expect(Scheduler).toHaveYielded([]);243 });244 it('passes the right context to class component lifecycles', () => {245 class App extends React.PureComponent {246 test() {}247 componentDidMount() {248 this.test();249 Scheduler.unstable_yieldValue('componentDidMount');250 }251 componentDidUpdate() {252 this.test();253 Scheduler.unstable_yieldValue('componentDidUpdate');254 }255 componentWillUnmount() {256 this.test();257 Scheduler.unstable_yieldValue('componentWillUnmount');258 }259 render() {260 return null;261 }262 }263 act(() => {264 ReactTestRenderer.create(<App />, {unstable_isConcurrent: true});265 });266 if (supportsDoubleInvokeEffects()) {267 expect(Scheduler).toHaveYielded([268 'componentDidMount',269 'componentWillUnmount',270 'componentDidMount',271 ]);272 } else {273 expect(Scheduler).toHaveYielded(['componentDidMount']);274 }275 });276 it('double invoking works for class components', () => {277 class App extends React.PureComponent {278 componentDidMount() {279 Scheduler.unstable_yieldValue('componentDidMount');280 }281 componentDidUpdate() {282 Scheduler.unstable_yieldValue('componentDidUpdate');283 }284 componentWillUnmount() {285 Scheduler.unstable_yieldValue('componentWillUnmount');286 }287 render() {288 return this.props.text;289 }290 }291 let renderer;292 act(() => {293 renderer = ReactTestRenderer.create(<App text={'mount'} />, {294 unstable_isConcurrent: true,295 });296 });297 if (supportsDoubleInvokeEffects()) {298 expect(Scheduler).toHaveYielded([299 'componentDidMount',300 'componentWillUnmount',301 'componentDidMount',302 ]);303 } else {304 expect(Scheduler).toHaveYielded(['componentDidMount']);305 }306 act(() => {307 renderer.update(<App text={'update'} />);308 });309 expect(Scheduler).toHaveYielded(['componentDidUpdate']);310 act(() => {311 renderer.unmount();312 });313 expect(Scheduler).toHaveYielded(['componentWillUnmount']);314 });315 it('should not double invoke class lifecycles in legacy mode', () => {316 class App extends React.PureComponent {317 componentDidMount() {318 Scheduler.unstable_yieldValue('componentDidMount');319 }320 componentDidUpdate() {321 Scheduler.unstable_yieldValue('componentDidUpdate');322 }323 componentWillUnmount() {324 Scheduler.unstable_yieldValue('componentWillUnmount');325 }326 render() {327 return this.props.text;328 }329 }330 act(() => {331 ReactTestRenderer.create(<App text={'mount'} />);332 });333 expect(Scheduler).toHaveYielded(['componentDidMount']);334 });335 it('double flushing passive effects only results in one double invoke', () => {336 function App({text}) {337 const [state, setState] = React.useState(0);338 React.useEffect(() => {339 if (state !== 1) {340 setState(1);341 }342 Scheduler.unstable_yieldValue('useEffect mount');343 return () => Scheduler.unstable_yieldValue('useEffect unmount');344 });345 React.useLayoutEffect(() => {346 Scheduler.unstable_yieldValue('useLayoutEffect mount');347 return () => Scheduler.unstable_yieldValue('useLayoutEffect unmount');348 });349 Scheduler.unstable_yieldValue(text);350 return text;351 }352 act(() => {353 ReactTestRenderer.create(<App text={'mount'} />, {354 unstable_isConcurrent: true,355 });356 });357 if (supportsDoubleInvokeEffects()) {358 expect(Scheduler).toHaveYielded([359 'mount',360 'useLayoutEffect mount',361 'useEffect mount',362 'useLayoutEffect unmount',363 'useEffect unmount',364 'useLayoutEffect mount',365 'useEffect mount',366 'mount',367 'useLayoutEffect unmount',368 'useLayoutEffect mount',369 'useEffect unmount',370 'useEffect mount',371 ]);372 } else {373 expect(Scheduler).toHaveYielded([374 'mount',375 'useLayoutEffect mount',376 'useEffect mount',377 'mount',378 'useLayoutEffect unmount',379 'useLayoutEffect mount',380 'useEffect unmount',381 'useEffect mount',382 ]);383 }384 });385 it('newly mounted components after initial mount get double invoked', () => {386 let _setShowChild;387 function Child() {388 React.useEffect(() => {389 Scheduler.unstable_yieldValue('Child useEffect mount');390 return () => Scheduler.unstable_yieldValue('Child useEffect unmount');391 });392 React.useLayoutEffect(() => {393 Scheduler.unstable_yieldValue('Child useLayoutEffect mount');394 return () =>395 Scheduler.unstable_yieldValue('Child useLayoutEffect unmount');396 });397 return null;398 }399 function App() {400 const [showChild, setShowChild] = React.useState(false);401 _setShowChild = setShowChild;402 React.useEffect(() => {403 Scheduler.unstable_yieldValue('App useEffect mount');404 return () => Scheduler.unstable_yieldValue('App useEffect unmount');405 });406 React.useLayoutEffect(() => {407 Scheduler.unstable_yieldValue('App useLayoutEffect mount');408 return () =>409 Scheduler.unstable_yieldValue('App useLayoutEffect unmount');410 });411 return showChild && <Child />;412 }413 act(() => {414 ReactTestRenderer.create(<App />, {unstable_isConcurrent: true});415 });416 if (supportsDoubleInvokeEffects()) {417 expect(Scheduler).toHaveYielded([418 'App useLayoutEffect mount',419 'App useEffect mount',420 'App useLayoutEffect unmount',421 'App useEffect unmount',422 'App useLayoutEffect mount',423 'App useEffect mount',424 ]);425 } else {426 expect(Scheduler).toHaveYielded([427 'App useLayoutEffect mount',428 'App useEffect mount',429 ]);430 }431 act(() => {432 _setShowChild(true);433 });434 if (supportsDoubleInvokeEffects()) {435 expect(Scheduler).toHaveYielded([436 'App useLayoutEffect unmount',437 'Child useLayoutEffect mount',438 'App useLayoutEffect mount',439 'App useEffect unmount',440 'Child useEffect mount',441 'App useEffect mount',442 'Child useLayoutEffect unmount',443 'Child useEffect unmount',444 'Child useLayoutEffect mount',445 'Child useEffect mount',446 ]);447 } else {448 expect(Scheduler).toHaveYielded([449 'App useLayoutEffect unmount',450 'Child useLayoutEffect mount',451 'App useLayoutEffect mount',452 'App useEffect unmount',453 'Child useEffect mount',454 'App useEffect mount',455 ]);456 }457 });458 it('classes and functions are double invoked together correctly', () => {459 class ClassChild extends React.PureComponent {460 componentDidMount() {461 Scheduler.unstable_yieldValue('componentDidMount');462 }463 componentWillUnmount() {464 Scheduler.unstable_yieldValue('componentWillUnmount');465 }466 render() {467 return this.props.text;468 }469 }470 function FunctionChild({text}) {471 React.useEffect(() => {472 Scheduler.unstable_yieldValue('useEffect mount');473 return () => Scheduler.unstable_yieldValue('useEffect unmount');474 });475 React.useLayoutEffect(() => {476 Scheduler.unstable_yieldValue('useLayoutEffect mount');477 return () => Scheduler.unstable_yieldValue('useLayoutEffect unmount');478 });479 return text;480 }481 function App({text}) {482 return (483 <>484 <ClassChild text={text} />485 <FunctionChild text={text} />...
ReactDoubleInvokeEvents-test.js
Source:ReactDoubleInvokeEvents-test.js
...28 );29 }30 it('should not double invoke effects in legacy mode', () => {31 function App({text}) {32 React.useEffect(() => {33 Scheduler.unstable_yieldValue('useEffect mount');34 return () => Scheduler.unstable_yieldValue('useEffect unmount');35 });36 React.useLayoutEffect(() => {37 Scheduler.unstable_yieldValue('useLayoutEffect mount');38 return () => Scheduler.unstable_yieldValue('useLayoutEffect unmount');39 });40 return text;41 }42 act(() => {43 ReactTestRenderer.create(<App text={'mount'} />);44 });45 expect(Scheduler).toHaveYielded([46 'useLayoutEffect mount',47 'useEffect mount',48 ]);49 });50 it('double invoking for effects works properly', () => {51 function App({text}) {52 React.useEffect(() => {53 Scheduler.unstable_yieldValue('useEffect mount');54 return () => Scheduler.unstable_yieldValue('useEffect unmount');55 });56 React.useLayoutEffect(() => {57 Scheduler.unstable_yieldValue('useLayoutEffect mount');58 return () => Scheduler.unstable_yieldValue('useLayoutEffect unmount');59 });60 return text;61 }62 let renderer;63 act(() => {64 renderer = ReactTestRenderer.create(<App text={'mount'} />, {65 unstable_isConcurrent: true,66 });67 });68 if (supportsDoubleInvokeEffects()) {69 expect(Scheduler).toHaveYielded([70 'useLayoutEffect mount',71 'useEffect mount',72 'useLayoutEffect unmount',73 'useEffect unmount',74 'useLayoutEffect mount',75 'useEffect mount',76 ]);77 } else {78 expect(Scheduler).toHaveYielded([79 'useLayoutEffect mount',80 'useEffect mount',81 ]);82 }83 act(() => {84 renderer.update(<App text={'update'} />);85 });86 expect(Scheduler).toHaveYielded([87 'useLayoutEffect unmount',88 'useLayoutEffect mount',89 'useEffect unmount',90 'useEffect mount',91 ]);92 act(() => {93 renderer.unmount();94 });95 expect(Scheduler).toHaveYielded([96 'useLayoutEffect unmount',97 'useEffect unmount',98 ]);99 });100 it('multiple effects are double invoked in the right order (all mounted, all unmounted, all remounted)', () => {101 function App({text}) {102 React.useEffect(() => {103 Scheduler.unstable_yieldValue('useEffect One mount');104 return () => Scheduler.unstable_yieldValue('useEffect One unmount');105 });106 React.useEffect(() => {107 Scheduler.unstable_yieldValue('useEffect Two mount');108 return () => Scheduler.unstable_yieldValue('useEffect Two unmount');109 });110 return text;111 }112 let renderer;113 act(() => {114 renderer = ReactTestRenderer.create(<App text={'mount'} />, {115 unstable_isConcurrent: true,116 });117 });118 if (supportsDoubleInvokeEffects()) {119 expect(Scheduler).toHaveYielded([120 'useEffect One mount',121 'useEffect Two mount',122 'useEffect One unmount',123 'useEffect Two unmount',124 'useEffect One mount',125 'useEffect Two mount',126 ]);127 } else {128 expect(Scheduler).toHaveYielded([129 'useEffect One mount',130 'useEffect Two mount',131 ]);132 }133 act(() => {134 renderer.update(<App text={'update'} />);135 });136 expect(Scheduler).toHaveYielded([137 'useEffect One unmount',138 'useEffect Two unmount',139 'useEffect One mount',140 'useEffect Two mount',141 ]);142 act(() => {143 renderer.unmount(null);144 });145 expect(Scheduler).toHaveYielded([146 'useEffect One unmount',147 'useEffect Two unmount',148 ]);149 });150 it('multiple layout effects are double invoked in the right order (all mounted, all unmounted, all remounted)', () => {151 function App({text}) {152 React.useLayoutEffect(() => {153 Scheduler.unstable_yieldValue('useLayoutEffect One mount');154 return () =>155 Scheduler.unstable_yieldValue('useLayoutEffect One unmount');156 });157 React.useLayoutEffect(() => {158 Scheduler.unstable_yieldValue('useLayoutEffect Two mount');159 return () =>160 Scheduler.unstable_yieldValue('useLayoutEffect Two unmount');161 });162 return text;163 }164 let renderer;165 act(() => {166 renderer = ReactTestRenderer.create(<App text={'mount'} />, {167 unstable_isConcurrent: true,168 });169 });170 if (supportsDoubleInvokeEffects()) {171 expect(Scheduler).toHaveYielded([172 'useLayoutEffect One mount',173 'useLayoutEffect Two mount',174 'useLayoutEffect One unmount',175 'useLayoutEffect Two unmount',176 'useLayoutEffect One mount',177 'useLayoutEffect Two mount',178 ]);179 } else {180 expect(Scheduler).toHaveYielded([181 'useLayoutEffect One mount',182 'useLayoutEffect Two mount',183 ]);184 }185 act(() => {186 renderer.update(<App text={'update'} />);187 });188 expect(Scheduler).toHaveYielded([189 'useLayoutEffect One unmount',190 'useLayoutEffect Two unmount',191 'useLayoutEffect One mount',192 'useLayoutEffect Two mount',193 ]);194 act(() => {195 renderer.unmount();196 });197 expect(Scheduler).toHaveYielded([198 'useLayoutEffect One unmount',199 'useLayoutEffect Two unmount',200 ]);201 });202 it('useEffect and useLayoutEffect is called twice when there is no unmount', () => {203 function App({text}) {204 React.useEffect(() => {205 Scheduler.unstable_yieldValue('useEffect mount');206 });207 React.useLayoutEffect(() => {208 Scheduler.unstable_yieldValue('useLayoutEffect mount');209 });210 return text;211 }212 let renderer;213 act(() => {214 renderer = ReactTestRenderer.create(<App text={'mount'} />, {215 unstable_isConcurrent: true,216 });217 });218 if (supportsDoubleInvokeEffects()) {219 expect(Scheduler).toHaveYielded([220 'useLayoutEffect mount',221 'useEffect mount',222 'useLayoutEffect mount',223 'useEffect mount',224 ]);225 } else {226 expect(Scheduler).toHaveYielded([227 'useLayoutEffect mount',228 'useEffect mount',229 ]);230 }231 act(() => {232 renderer.update(<App text={'update'} />);233 });234 expect(Scheduler).toHaveYielded([235 'useLayoutEffect mount',236 'useEffect mount',237 ]);238 act(() => {239 renderer.unmount();240 });241 expect(Scheduler).toHaveYielded([]);242 });243 it('passes the right context to class component lifecycles', () => {244 class App extends React.PureComponent {245 test() {}246 componentDidMount() {247 this.test();248 Scheduler.unstable_yieldValue('componentDidMount');249 }250 componentDidUpdate() {251 this.test();252 Scheduler.unstable_yieldValue('componentDidUpdate');253 }254 componentWillUnmount() {255 this.test();256 Scheduler.unstable_yieldValue('componentWillUnmount');257 }258 render() {259 return null;260 }261 }262 act(() => {263 ReactTestRenderer.create(<App />, {unstable_isConcurrent: true});264 });265 if (supportsDoubleInvokeEffects()) {266 expect(Scheduler).toHaveYielded([267 'componentDidMount',268 'componentWillUnmount',269 'componentDidMount',270 ]);271 } else {272 expect(Scheduler).toHaveYielded(['componentDidMount']);273 }274 });275 it('double invoking works for class components', () => {276 class App extends React.PureComponent {277 componentDidMount() {278 Scheduler.unstable_yieldValue('componentDidMount');279 }280 componentDidUpdate() {281 Scheduler.unstable_yieldValue('componentDidUpdate');282 }283 componentWillUnmount() {284 Scheduler.unstable_yieldValue('componentWillUnmount');285 }286 render() {287 return this.props.text;288 }289 }290 let renderer;291 act(() => {292 renderer = ReactTestRenderer.create(<App text={'mount'} />, {293 unstable_isConcurrent: true,294 });295 });296 if (supportsDoubleInvokeEffects()) {297 expect(Scheduler).toHaveYielded([298 'componentDidMount',299 'componentWillUnmount',300 'componentDidMount',301 ]);302 } else {303 expect(Scheduler).toHaveYielded(['componentDidMount']);304 }305 act(() => {306 renderer.update(<App text={'update'} />);307 });308 expect(Scheduler).toHaveYielded(['componentDidUpdate']);309 act(() => {310 renderer.unmount();311 });312 expect(Scheduler).toHaveYielded(['componentWillUnmount']);313 });314 it('should not double invoke class lifecycles in legacy mode', () => {315 class App extends React.PureComponent {316 componentDidMount() {317 Scheduler.unstable_yieldValue('componentDidMount');318 }319 componentDidUpdate() {320 Scheduler.unstable_yieldValue('componentDidUpdate');321 }322 componentWillUnmount() {323 Scheduler.unstable_yieldValue('componentWillUnmount');324 }325 render() {326 return this.props.text;327 }328 }329 act(() => {330 ReactTestRenderer.create(<App text={'mount'} />);331 });332 expect(Scheduler).toHaveYielded(['componentDidMount']);333 });334 it('double flushing passive effects only results in one double invoke', () => {335 function App({text}) {336 const [state, setState] = React.useState(0);337 React.useEffect(() => {338 if (state !== 1) {339 setState(1);340 }341 Scheduler.unstable_yieldValue('useEffect mount');342 return () => Scheduler.unstable_yieldValue('useEffect unmount');343 });344 React.useLayoutEffect(() => {345 Scheduler.unstable_yieldValue('useLayoutEffect mount');346 return () => Scheduler.unstable_yieldValue('useLayoutEffect unmount');347 });348 Scheduler.unstable_yieldValue(text);349 return text;350 }351 act(() => {352 ReactTestRenderer.create(<App text={'mount'} />, {353 unstable_isConcurrent: true,354 });355 });356 if (supportsDoubleInvokeEffects()) {357 expect(Scheduler).toHaveYielded([358 'mount',359 'useLayoutEffect mount',360 'useEffect mount',361 'useLayoutEffect unmount',362 'useEffect unmount',363 'useLayoutEffect mount',364 'useEffect mount',365 'mount',366 'useLayoutEffect unmount',367 'useLayoutEffect mount',368 'useEffect unmount',369 'useEffect mount',370 ]);371 } else {372 expect(Scheduler).toHaveYielded([373 'mount',374 'useLayoutEffect mount',375 'useEffect mount',376 'mount',377 'useLayoutEffect unmount',378 'useLayoutEffect mount',379 'useEffect unmount',380 'useEffect mount',381 ]);382 }383 });384 it('newly mounted components after initial mount get double invoked', () => {385 let _setShowChild;386 function Child() {387 React.useEffect(() => {388 Scheduler.unstable_yieldValue('Child useEffect mount');389 return () => Scheduler.unstable_yieldValue('Child useEffect unmount');390 });391 React.useLayoutEffect(() => {392 Scheduler.unstable_yieldValue('Child useLayoutEffect mount');393 return () =>394 Scheduler.unstable_yieldValue('Child useLayoutEffect unmount');395 });396 return null;397 }398 function App() {399 const [showChild, setShowChild] = React.useState(false);400 _setShowChild = setShowChild;401 React.useEffect(() => {402 Scheduler.unstable_yieldValue('App useEffect mount');403 return () => Scheduler.unstable_yieldValue('App useEffect unmount');404 });405 React.useLayoutEffect(() => {406 Scheduler.unstable_yieldValue('App useLayoutEffect mount');407 return () =>408 Scheduler.unstable_yieldValue('App useLayoutEffect unmount');409 });410 return showChild && <Child />;411 }412 act(() => {413 ReactTestRenderer.create(<App />, {unstable_isConcurrent: true});414 });415 if (supportsDoubleInvokeEffects()) {416 expect(Scheduler).toHaveYielded([417 'App useLayoutEffect mount',418 'App useEffect mount',419 'App useLayoutEffect unmount',420 'App useEffect unmount',421 'App useLayoutEffect mount',422 'App useEffect mount',423 ]);424 } else {425 expect(Scheduler).toHaveYielded([426 'App useLayoutEffect mount',427 'App useEffect mount',428 ]);429 }430 act(() => {431 _setShowChild(true);432 });433 if (supportsDoubleInvokeEffects()) {434 expect(Scheduler).toHaveYielded([435 'App useLayoutEffect unmount',436 'Child useLayoutEffect mount',437 'App useLayoutEffect mount',438 'App useEffect unmount',439 'Child useEffect mount',440 'App useEffect mount',441 'Child useLayoutEffect unmount',442 'Child useEffect unmount',443 'Child useLayoutEffect mount',444 'Child useEffect mount',445 ]);446 } else {447 expect(Scheduler).toHaveYielded([448 'App useLayoutEffect unmount',449 'Child useLayoutEffect mount',450 'App useLayoutEffect mount',451 'App useEffect unmount',452 'Child useEffect mount',453 'App useEffect mount',454 ]);455 }456 });457 it('classes and functions are double invoked together correctly', () => {458 class ClassChild extends React.PureComponent {459 componentDidMount() {460 Scheduler.unstable_yieldValue('componentDidMount');461 }462 componentWillUnmount() {463 Scheduler.unstable_yieldValue('componentWillUnmount');464 }465 render() {466 return this.props.text;467 }468 }469 function FunctionChild({text}) {470 React.useEffect(() => {471 Scheduler.unstable_yieldValue('useEffect mount');472 return () => Scheduler.unstable_yieldValue('useEffect unmount');473 });474 React.useLayoutEffect(() => {475 Scheduler.unstable_yieldValue('useLayoutEffect mount');476 return () => Scheduler.unstable_yieldValue('useLayoutEffect unmount');477 });478 return text;479 }480 function App({text}) {481 return (482 <>483 <ClassChild text={text} />484 <FunctionChild text={text} />...
ReactDoubleInvokeEvents-test.internal.js
Source:ReactDoubleInvokeEvents-test.internal.js
...21 ReactFeatureFlags.enableDoubleInvokingEffects = __VARIANT__;22 });23 it('double invoking for effects works properly', () => {24 function App({text}) {25 React.useEffect(() => {26 Scheduler.unstable_yieldValue('useEffect mount');27 return () => Scheduler.unstable_yieldValue('useEffect unmount');28 });29 React.useLayoutEffect(() => {30 Scheduler.unstable_yieldValue('useLayoutEffect mount');31 return () => Scheduler.unstable_yieldValue('useLayoutEffect unmount');32 });33 return text;34 }35 ReactNoop.act(() => {36 ReactNoop.render(<App text={'mount'} />);37 });38 if (__DEV__ && __VARIANT__) {39 expect(Scheduler).toHaveYielded([40 'useLayoutEffect mount',41 'useEffect mount',42 'useLayoutEffect unmount',43 'useEffect unmount',44 'useLayoutEffect mount',45 'useEffect mount',46 ]);47 } else {48 expect(Scheduler).toHaveYielded([49 'useLayoutEffect mount',50 'useEffect mount',51 ]);52 }53 ReactNoop.act(() => {54 ReactNoop.render(<App text={'update'} />);55 });56 expect(Scheduler).toHaveYielded([57 'useLayoutEffect unmount',58 'useLayoutEffect mount',59 'useEffect unmount',60 'useEffect mount',61 ]);62 ReactNoop.act(() => {63 ReactNoop.render(null);64 });65 expect(Scheduler).toHaveYielded([66 'useLayoutEffect unmount',67 'useEffect unmount',68 ]);69 });70 it('multiple effects are double invoked in the right order (all mounted, all unmounted, all remounted)', () => {71 function App({text}) {72 React.useEffect(() => {73 Scheduler.unstable_yieldValue('useEffect One mount');74 return () => Scheduler.unstable_yieldValue('useEffect One unmount');75 });76 React.useEffect(() => {77 Scheduler.unstable_yieldValue('useEffect Two mount');78 return () => Scheduler.unstable_yieldValue('useEffect Two unmount');79 });80 return text;81 }82 ReactNoop.act(() => {83 ReactNoop.render(<App text={'mount'} />);84 });85 if (__DEV__ && __VARIANT__) {86 expect(Scheduler).toHaveYielded([87 'useEffect One mount',88 'useEffect Two mount',89 'useEffect One unmount',90 'useEffect Two unmount',91 'useEffect One mount',92 'useEffect Two mount',93 ]);94 } else {95 expect(Scheduler).toHaveYielded([96 'useEffect One mount',97 'useEffect Two mount',98 ]);99 }100 ReactNoop.act(() => {101 ReactNoop.render(<App text={'update'} />);102 });103 expect(Scheduler).toHaveYielded([104 'useEffect One unmount',105 'useEffect Two unmount',106 'useEffect One mount',107 'useEffect Two mount',108 ]);109 ReactNoop.act(() => {110 ReactNoop.render(null);111 });112 expect(Scheduler).toHaveYielded([113 'useEffect One unmount',114 'useEffect Two unmount',115 ]);116 });117 it('multiple layout effects are double invoked in the right order (all mounted, all unmounted, all remounted)', () => {118 function App({text}) {119 React.useLayoutEffect(() => {120 Scheduler.unstable_yieldValue('useLayoutEffect One mount');121 return () =>122 Scheduler.unstable_yieldValue('useLayoutEffect One unmount');123 });124 React.useLayoutEffect(() => {125 Scheduler.unstable_yieldValue('useLayoutEffect Two mount');126 return () =>127 Scheduler.unstable_yieldValue('useLayoutEffect Two unmount');128 });129 return text;130 }131 ReactNoop.act(() => {132 ReactNoop.render(<App text={'mount'} />);133 });134 if (__DEV__ && __VARIANT__) {135 expect(Scheduler).toHaveYielded([136 'useLayoutEffect One mount',137 'useLayoutEffect Two mount',138 'useLayoutEffect One unmount',139 'useLayoutEffect Two unmount',140 'useLayoutEffect One mount',141 'useLayoutEffect Two mount',142 ]);143 } else {144 expect(Scheduler).toHaveYielded([145 'useLayoutEffect One mount',146 'useLayoutEffect Two mount',147 ]);148 }149 ReactNoop.act(() => {150 ReactNoop.render(<App text={'update'} />);151 });152 expect(Scheduler).toHaveYielded([153 'useLayoutEffect One unmount',154 'useLayoutEffect Two unmount',155 'useLayoutEffect One mount',156 'useLayoutEffect Two mount',157 ]);158 ReactNoop.act(() => {159 ReactNoop.render(null);160 });161 expect(Scheduler).toHaveYielded([162 'useLayoutEffect One unmount',163 'useLayoutEffect Two unmount',164 ]);165 });166 it('useEffect and useLayoutEffect is called twice when there is no unmount', () => {167 function App({text}) {168 React.useEffect(() => {169 Scheduler.unstable_yieldValue('useEffect mount');170 });171 React.useLayoutEffect(() => {172 Scheduler.unstable_yieldValue('useLayoutEffect mount');173 });174 return text;175 }176 ReactNoop.act(() => {177 ReactNoop.render(<App text={'mount'} />);178 });179 if (__DEV__ && __VARIANT__) {180 expect(Scheduler).toHaveYielded([181 'useLayoutEffect mount',182 'useEffect mount',183 'useLayoutEffect mount',184 'useEffect mount',185 ]);186 } else {187 expect(Scheduler).toHaveYielded([188 'useLayoutEffect mount',189 'useEffect mount',190 ]);191 }192 ReactNoop.act(() => {193 ReactNoop.render(<App text={'update'} />);194 });195 expect(Scheduler).toHaveYielded([196 'useLayoutEffect mount',197 'useEffect mount',198 ]);199 ReactNoop.act(() => {200 ReactNoop.render(null);201 });202 expect(Scheduler).toHaveYielded([]);203 });204 it('passes the right context to class component lifecycles', () => {205 class App extends React.PureComponent {206 test() {}207 componentDidMount() {208 this.test();209 Scheduler.unstable_yieldValue('componentDidMount');210 }211 componentDidUpdate() {212 this.test();213 Scheduler.unstable_yieldValue('componentDidUpdate');214 }215 componentWillUnmount() {216 this.test();217 Scheduler.unstable_yieldValue('componentWillUnmount');218 }219 render() {220 return null;221 }222 }223 ReactNoop.act(() => {224 ReactNoop.render(<App />);225 });226 if (__DEV__ && __VARIANT__) {227 expect(Scheduler).toHaveYielded([228 'componentDidMount',229 'componentWillUnmount',230 'componentDidMount',231 ]);232 } else {233 expect(Scheduler).toHaveYielded(['componentDidMount']);234 }235 });236 it('double invoking works for class components', () => {237 class App extends React.PureComponent {238 componentDidMount() {239 Scheduler.unstable_yieldValue('componentDidMount');240 }241 componentDidUpdate() {242 Scheduler.unstable_yieldValue('componentDidUpdate');243 }244 componentWillUnmount() {245 Scheduler.unstable_yieldValue('componentWillUnmount');246 }247 render() {248 return this.props.text;249 }250 }251 ReactNoop.act(() => {252 ReactNoop.render(<App text={'mount'} />);253 });254 if (__DEV__ && __VARIANT__) {255 expect(Scheduler).toHaveYielded([256 'componentDidMount',257 'componentWillUnmount',258 'componentDidMount',259 ]);260 } else {261 expect(Scheduler).toHaveYielded(['componentDidMount']);262 }263 ReactNoop.act(() => {264 ReactNoop.render(<App text={'update'} />);265 });266 expect(Scheduler).toHaveYielded(['componentDidUpdate']);267 ReactNoop.act(() => {268 ReactNoop.render(null);269 });270 expect(Scheduler).toHaveYielded(['componentWillUnmount']);271 });272 it('double flushing passive effects only results in one double invoke', () => {273 function App({text}) {274 const [state, setState] = React.useState(0);275 React.useEffect(() => {276 if (state !== 1) {277 setState(1);278 }279 Scheduler.unstable_yieldValue('useEffect mount');280 return () => Scheduler.unstable_yieldValue('useEffect unmount');281 });282 React.useLayoutEffect(() => {283 Scheduler.unstable_yieldValue('useLayoutEffect mount');284 return () => Scheduler.unstable_yieldValue('useLayoutEffect unmount');285 });286 Scheduler.unstable_yieldValue(text);287 return text;288 }289 ReactNoop.act(() => {290 ReactNoop.render(<App text={'mount'} />);291 });292 if (__DEV__ && __VARIANT__) {293 expect(Scheduler).toHaveYielded([294 'mount',295 'useLayoutEffect mount',296 'useEffect mount',297 'useLayoutEffect unmount',298 'useEffect unmount',299 'useLayoutEffect mount',300 'useEffect mount',301 'mount',302 'useLayoutEffect unmount',303 'useLayoutEffect mount',304 'useEffect unmount',305 'useEffect mount',306 ]);307 } else {308 expect(Scheduler).toHaveYielded([309 'mount',310 'useLayoutEffect mount',311 'useEffect mount',312 'mount',313 'useLayoutEffect unmount',314 'useLayoutEffect mount',315 'useEffect unmount',316 'useEffect mount',317 ]);318 }319 });320 it('newly mounted components after initial mount get double invoked', () => {321 let _setShowChild;322 function Child() {323 React.useEffect(() => {324 Scheduler.unstable_yieldValue('Child useEffect mount');325 return () => Scheduler.unstable_yieldValue('Child useEffect unmount');326 });327 React.useLayoutEffect(() => {328 Scheduler.unstable_yieldValue('Child useLayoutEffect mount');329 return () =>330 Scheduler.unstable_yieldValue('Child useLayoutEffect unmount');331 });332 return null;333 }334 function App() {335 const [showChild, setShowChild] = React.useState(false);336 _setShowChild = setShowChild;337 React.useEffect(() => {338 Scheduler.unstable_yieldValue('App useEffect mount');339 return () => Scheduler.unstable_yieldValue('App useEffect unmount');340 });341 React.useLayoutEffect(() => {342 Scheduler.unstable_yieldValue('App useLayoutEffect mount');343 return () =>344 Scheduler.unstable_yieldValue('App useLayoutEffect unmount');345 });346 return showChild && <Child />;347 }348 ReactNoop.act(() => {349 ReactNoop.render(<App />);350 });351 if (__DEV__ && __VARIANT__) {352 expect(Scheduler).toHaveYielded([353 'App useLayoutEffect mount',354 'App useEffect mount',355 'App useLayoutEffect unmount',356 'App useEffect unmount',357 'App useLayoutEffect mount',358 'App useEffect mount',359 ]);360 } else {361 expect(Scheduler).toHaveYielded([362 'App useLayoutEffect mount',363 'App useEffect mount',364 ]);365 }366 ReactNoop.act(() => {367 _setShowChild(true);368 });369 if (__DEV__ && __VARIANT__) {370 expect(Scheduler).toHaveYielded([371 'App useLayoutEffect unmount',372 'Child useLayoutEffect mount',373 'App useLayoutEffect mount',374 'App useEffect unmount',375 'Child useEffect mount',376 'App useEffect mount',377 'Child useLayoutEffect unmount',378 'Child useEffect unmount',379 'Child useLayoutEffect mount',380 'Child useEffect mount',381 ]);382 } else {383 expect(Scheduler).toHaveYielded([384 'App useLayoutEffect unmount',385 'Child useLayoutEffect mount',386 'App useLayoutEffect mount',387 'App useEffect unmount',388 'Child useEffect mount',389 'App useEffect mount',390 ]);391 }392 });393 it('classes and functions are double invoked together correctly', () => {394 class ClassChild extends React.PureComponent {395 componentDidMount() {396 Scheduler.unstable_yieldValue('componentDidMount');397 }398 componentWillUnmount() {399 Scheduler.unstable_yieldValue('componentWillUnmount');400 }401 render() {402 return this.props.text;403 }404 }405 function FunctionChild({text}) {406 React.useEffect(() => {407 Scheduler.unstable_yieldValue('useEffect mount');408 return () => Scheduler.unstable_yieldValue('useEffect unmount');409 });410 React.useLayoutEffect(() => {411 Scheduler.unstable_yieldValue('useLayoutEffect mount');412 return () => Scheduler.unstable_yieldValue('useLayoutEffect unmount');413 });414 return text;415 }416 function App({text}) {417 return (418 <>419 <ClassChild text={text} />420 <FunctionChild text={text} />...
ReactStrictMode-test.internal.js
Source:ReactStrictMode-test.internal.js
...24 beforeEach(() => {25 log = [];26 });27 function Component({label}) {28 React.useEffect(() => {29 log.push(`${label}: useEffect mount`);30 return () => log.push(`${label}: useEffect unmount`);31 });32 React.useLayoutEffect(() => {33 log.push(`${label}: useLayoutEffect mount`);34 return () => log.push(`${label}: useLayoutEffect unmount`);35 });36 log.push(`${label}: render`);37 return null;38 }39 it('should default to not strict', () => {40 act(() => {41 const container = document.createElement('div');42 const root = ReactDOMClient.createRoot(container);...
hook-flow.js
Source:hook-flow.js
...10 const [count, setCount] = React.useState(() => {11 console.log('%c Child: useState(() => 0)', 'color: tomato')12 return 013 })14 React.useEffect(() => {15 console.log('%c Child: useEffect(() => {})', 'color: LightCoral')16 return () => {17 console.log(18 '%c Child: useEffect(() => {}) cleanup ð§¹',19 'color: LightCoral',20 )21 }22 })23 React.useEffect(() => {24 console.log(25 '%c Child: useEffect(() => {}, [])',26 'color: MediumTurquoise',27 )28 return () => {29 console.log(30 '%c Child: useEffect(() => {}, []) cleanup ð§¹',31 'color: MediumTurquoise',32 )33 }34 }, [])35 React.useEffect(() => {36 console.log('%c Child: useEffect(() => {}, [count])', 'color: HotPink')37 return () => {38 console.log(39 '%c Child: useEffect(() => {}, [count]) cleanup ð§¹',40 'color: HotPink',41 )42 }43 }, [count])44 const element = (45 <button onClick={() => setCount(previousCount => previousCount + 1)}>46 {count}47 </button>48 )49 console.log('%c Child: render end', 'color: MediumSpringGreen')50 return element51}52function App() {53 console.log('%cApp: render start', 'color: MediumSpringGreen')54 const [showChild, setShowChild] = React.useState(() => {55 console.log('%cApp: useState(() => false)', 'color: tomato')56 return false57 })58 React.useEffect(() => {59 console.log('%cApp: useEffect(() => {})', 'color: LightCoral')60 return () => {61 console.log('%cApp: useEffect(() => {}) cleanup ð§¹', 'color: LightCoral')62 }63 })64 React.useEffect(() => {65 console.log('%cApp: useEffect(() => {}, [])', 'color: MediumTurquoise')66 return () => {67 console.log(68 '%cApp: useEffect(() => {}, []) cleanup ð§¹',69 'color: MediumTurquoise',70 )71 }72 }, [])73 React.useEffect(() => {74 console.log('%cApp: useEffect(() => {}, [showChild])', 'color: HotPink')75 return () => {76 console.log(77 '%cApp: useEffect(() => {}, [showChild]) cleanup ð§¹',78 'color: HotPink',79 )80 }81 }, [showChild])82 const element = (83 <>84 <label>85 <input86 type="checkbox"87 checked={showChild}88 onChange={e => setShowChild(e.target.checked)}89 />{' '}90 show child91 </label>...
form1.jsx
Source:form1.jsx
...5 const [count, setCount] = useState(() => {6 console.log("%c Child: useState callback", "color: tomato");7 return 0;8 });9 useEffect(() => {10 console.log("%c Child: useEffect no deps", "color: LightCoral");11 return () => {12 console.log(13 "%c Child: useEffect no deps cleanup",14 "color: LightCoral"15 );16 };17 });18 useEffect(() => {19 console.log("%c Child: useEffect empty deps", "color: MediumTurquoise");20 return () => {21 console.log(22 "%c Child: useEffect empty deps cleanup",23 "color: MediumTurquoise"24 );25 };26 }, []);27 useEffect(() => {28 console.log("%c Child: useEffect with dep", "color: HotPink");29 return () => {30 console.log("%c Child: useEffect with dep cleanup", "color: HotPink");31 };32 }, [count]);33 const element = (34 <button onClick={() => setCount((previousCount) => previousCount + 1)}>35 {count}36 </button>37 );38 console.log("%c Child: render end", "color: MediumSpringGreen");39 return element;40}41function App() {42 console.log("%cApp: render start", "color: MediumSpringGreen");43 const [showChild, setShowChild] = useState(() => {44 console.log("%cApp: useState callback", "color: tomato");45 return false;46 });47 useEffect(() => {48 console.log("%cApp: useEffect no deps", "color: LightCoral");49 return () => {50 console.log("%cApp: useEffect no deps cleanup", "color: LightCoral");51 };52 });53 useEffect(() => {54 console.log("%cApp: useEffect empty deps", "color: MediumTurquoise");55 return () => {56 console.log(57 "%cApp: useEffect empty deps cleanup",58 "color: MediumTurquoise"59 );60 };61 }, []);62 useEffect(() => {63 console.log("%cApp: useEffect with dep", "color: HotPink");64 return () => {65 console.log("%cApp: useEffect with dep cleanup", "color: HotPink");66 };67 }, [showChild]);68 const element = (69 <>70 <label>71 <input72 type="checkbox"73 checked={showChild}74 onChange={(e) => setShowChild(e.target.checked)}75 />{" "}76 show child...
Using AI Code Generation
1const { chromium } = require('playwright');2(async () => {3 const browser = await chromium.launch({ headless: false });4 const context = await browser.newContext();5 const page = await context.newPage();6 await page.screenshot({ path: `example.png` });7 await browser.close();8})();9const { chromium } = require('playwright-internal-api');10const { chromium } = require('playwright-internal-api');11(async () => {12 const browser = await chromium.launch({ headless: false });13 const context = await browser.newContext();14 const page = await context.newPage();15 await page.screenshot({ path: `example.png` });16 await browser.close();17})();18[MIT](LICENSE)
Using AI Code Generation
1const { chromium } = require('playwright');2(async () => {3 const browser = await chromium.launch({ headless: false });4 const context = await browser.newContext();5 const page = await context.newPage();6 await page.waitForTimeout(5000);7 await page.click('#search');8 await page.type('#search', 'Playwright');9 await page.waitForTimeout(5000);10 await page.click('#search-icon-legacy');11 await page.waitForTimeout(5000);12 await page.click('text="Playwright: End-to-End Testing for Modern Web Apps"');13 await page.waitForTimeout(5000);14 await page.click('text="Playwright: End-to-End Testing for Modern Web Apps"');15 await page.waitForTimeout(5000);16 await page.click('text="Playwright: End-to-End Testing for Modern Web Apps"');17 await page.waitForTimeout(5000);18 await page.click('text="Playwright: End-to-End Testing for Modern Web Apps"');19 await page.waitForTimeout(5000);20 await page.click('text="Playwright: End-to-End Testing for Modern Web Apps"');21 await page.waitForTimeout(5000);22 await page.click('text="Playwright: End-to-End Testing for Modern Web Apps"');23 await page.waitForTimeout(5000);24 await page.click('text="Playwright: End-to-End Testing for Modern Web Apps"');25 await page.waitForTimeout(5000);26 await page.click('text="Playwright: End-to-End Testing for Modern Web Apps"');27 await page.waitForTimeout(5000);28 await page.click('text="Playwright: End-to-End Testing for Modern Web Apps"');29 await page.waitForTimeout(5000);30 await browser.close();31})();32const { chromium } = require('playwright');33(async () => {34 const browser = await chromium.launch({ headless: false });35 const context = await browser.newContext();36 const page = await context.newPage();37 await page.waitForTimeout(5000);38 await page.click('#search');39 await page.type('#search', 'Playwright');40 await page.waitForTimeout(5000
Using AI Code Generation
1const { chromium } = require('playwright');2(async () => {3 const browser = await chromium.launch({ headless: false });4 const context = await browser.newContext();5 const page = await context.newPage();6 await page.fill('input[aria-label="Search"]', 'Playwright');7 await page.keyboard.press('Enter');8 await page.waitForNavigation();9 await page.screenshot({ path: `example.png` });10 await browser.close();11})();
Using AI Code Generation
1const { chromium } = require('playwright');2(async () => {3 const browser = await chromium.launch({ headless: false, slowMo: 50 });4 const context = await browser.newContext();5 const page = await context.newPage();6 await page.screenshot({ path: `example.png` });7 await browser.close();8})();
Using AI Code Generation
1const { chromium } = require('playwright');2(async () => {3 const browser = await chromium.launch();4 const context = await browser.newContext();5 const page = await context.newPage();6 page.on('console', msg => console.log('PAGE LOG:', msg.text()));7 await page.evaluate(() => {8 console.log('hello from page');9 window.__PW_DEBUG__.useEffect(() => {10 console.log('hello from useEffect');11 });12 });13 await browser.close();14})();
Using AI Code Generation
1const { chromium } = require('playwright');2const { expect } = require('chai');3(async () => {4 const browser = await chromium.launch({ headless: false, slowMo: 50 });5 const context = await browser.newContext();6 const page = await context.newPage();7 await page.fill('input[name="q"]', 'My User Agent');8 await page.click('input[name="btnK"]');9 await page.waitForSelector('text=My User Agent - UserAgentString.com');10 await page.click('text=My User Agent - UserAgentString.com');11 const userAgent = await page.evaluate(() => navigator.userAgent);12 expect(userAgent).to.contain('HeadlessChrome');13 await page.screenshot({ path: `example.png` });14 await browser.close();15})();
Using AI Code Generation
1const { chromium } = require('playwright');2(async () => {3 const browser = await chromium.launch({ headless: false });4 const page = await browser.newPage();5 await page.waitForSelector('text=News');6 await page.click('text=News');7 await page.waitForSelector('text=Coronavirus');8 await page.click('text=Coronavirus');9 await page.waitForSelector('text=Coronavirus: Latest Updates');10 await page.click('text=Coronavirus: Latest Updates');11 await page.waitForSelector('text=Coronavirus: Latest Updates');12 await page.click('text=Coronavirus: Latest Updates');13 await page.waitForSelector('text=Coronavirus: Latest Updates');14 await page.click('text=Coronavirus: Latest Updates');15 await page.waitForSelector('text=Coronavirus: Latest Updates');16 await page.click('text=Coronavirus: Latest Updates');17 await page.waitForSelector('text=Coronavirus: Latest Updates');18 await page.click('text=Coronavirus: Latest Updates');19 await page.waitForSelector('text=Coronavirus: Latest Updates');20 await page.click('text=Coronavirus: Latest Updates');21 await page.waitForSelector('text=Coronavirus: Latest Updates');22 await page.click('text=Coronavirus: Latest Updates');23 await page.waitForSelector('text=Coronavirus: Latest Updates');24 await page.click('text=Coronavirus: Latest Updates');25 await page.waitForSelector('text=Coronavirus: Latest Updates');26 await page.click('text=Coronavirus: Latest Updates');27 await page.waitForSelector('text=Coronavirus: Latest Updates');28 await page.click('text=Coronavirus: Latest Updates');29 await browser.close();30})();
Using AI Code Generation
1const {chromium} = require('playwright');2const {test, expect} = require('@playwright/test');3test('useEffect', async ({ page }) => {4 const [video] = await page.$$('.hero-video video');5 const videoUrl = await video.getAttribute('src');6});
Using AI Code Generation
1const { chromium } = require('playwright');2const { expect } = require('chai');3const { test, expect: _expect } = require('@playwright/test');4const { it } = require('mocha');5test.describe('Playwright', () => {6 test.beforeEach(async ({ page }) => {7 });8 test('should login to the page', async ({ page }) => {9 await page.fill('#username', 'tomsmith');10 await page.fill('#password', 'SuperSecretPassword!');11 await page.click('button[type="submit"]');12 expect(await page.innerText('.flash.success')).to.contain('You logged into a secure area!');13 });14 test('should logout from the page', async ({ page }) => {15 await page.fill('#username', 'tomsmith');16 await page.fill('#password', 'SuperSecretPassword!');17 await page.click('button[type="submit"]');18 await page.click('a[href="/logout"]');19 expect(await page.innerText('.flash.success')).to.contain('You logged out of the secure area!');20 });21});22const { chromium } = require('playwright');23const { expect } = require('chai');24const { test, expect: _expect } = require('@playwright/test');25const { it } = require('mocha');26test.describe('Playwright', () => {27 test.beforeEach(async ({ page }) => {28 });29 test('should login to the page', async ({ page }) => {30 await page.fill('#username', 'tomsmith');31 await page.fill('#password', 'SuperSecretPassword!');32 await page.click('button[type="submit"]');33 _expect(await page.innerText('.flash.success')).to.contain('You logged into a secure area!');34 });35 test('should logout from the page', async ({ page }) => {36 await page.fill('#username', 'tomsmith');37 await page.fill('#password', 'SuperSecretPassword!');38 await page.click('button[type="submit"]');39 await page.click('a[href="/logout"]');40 _expect(await page.innerText('.flash.success')).to.contain('You logged out of the secure area!');41 });42});43const { chromium } = require('
LambdaTest’s Playwright tutorial will give you a broader idea about the Playwright automation framework, its unique features, and use cases with examples to exceed your understanding of Playwright testing. This tutorial will give A to Z guidance, from installing the Playwright framework to some best practices and advanced concepts.
Get 100 minutes of automation test minutes FREE!!