Skip to content

Commit 9ab6ebc

Browse files
committed
Unify experimental check for useEvent
1 parent 76c096c commit 9ab6ebc

File tree

3 files changed

+145
-132
lines changed

3 files changed

+145
-132
lines changed

packages/eslint-plugin-react-hooks/__tests__/ESLintRulesOfHooks-test.js

Lines changed: 133 additions & 124 deletions
Original file line numberDiff line numberDiff line change
@@ -406,87 +406,6 @@ const tests = {
406406
const [myState, setMyState] = useState(null);
407407
}
408408
`,
409-
`
410-
// Valid because functions created with useEvent can be called in a useEffect.
411-
function MyComponent({ theme }) {
412-
const onClick = useEvent(() => {
413-
showNotification(theme);
414-
});
415-
useEffect(() => {
416-
onClick();
417-
});
418-
}
419-
`,
420-
`
421-
// Valid because functions created with useEvent can be called in closures.
422-
function MyComponent({ theme }) {
423-
const onClick = useEvent(() => {
424-
showNotification(theme);
425-
});
426-
return <Child onClick={() => onClick()}></Child>;
427-
}
428-
`,
429-
`
430-
// Valid because functions created with useEvent can be called in closures.
431-
function MyComponent({ theme }) {
432-
const onClick = useEvent(() => {
433-
showNotification(theme);
434-
});
435-
const onClick2 = () => { onClick() };
436-
const onClick3 = useCallback(() => onClick(), []);
437-
return <>
438-
<Child onClick={onClick2}></Child>
439-
<Child onClick={onClick3}></Child>
440-
</>;
441-
}
442-
`,
443-
`
444-
// Valid because functions created with useEvent can be passed by reference in useEffect
445-
// and useEvent.
446-
function MyComponent({ theme }) {
447-
const onClick = useEvent(() => {
448-
showNotification(theme);
449-
});
450-
const onClick2 = useEvent(() => {
451-
debounce(onClick);
452-
});
453-
useEffect(() => {
454-
let id = setInterval(onClick, 100);
455-
return () => clearInterval(onClick);
456-
}, []);
457-
return <Child onClick={() => onClick2()} />
458-
}
459-
`,
460-
`
461-
const MyComponent = ({theme}) => {
462-
const onClick = useEvent(() => {
463-
showNotification(theme);
464-
});
465-
return <Child onClick={() => onClick()}></Child>;
466-
};
467-
`,
468-
`
469-
function MyComponent({ theme }) {
470-
const notificationService = useNotifications();
471-
const showNotification = useEvent((text) => {
472-
notificationService.notify(theme, text);
473-
});
474-
const onClick = useEvent((text) => {
475-
showNotification(text);
476-
});
477-
return <Child onClick={(text) => onClick(text)} />
478-
}
479-
`,
480-
`
481-
function MyComponent({ theme }) {
482-
useEffect(() => {
483-
onClick();
484-
});
485-
const onClick = useEvent(() => {
486-
showNotification(theme);
487-
});
488-
}
489-
`,
490409
],
491410
invalid: [
492411
{
@@ -1052,66 +971,156 @@ const tests = {
1052971
`,
1053972
errors: [classError('useState')],
1054973
},
974+
],
975+
};
976+
977+
if (__EXPERIMENTAL__) {
978+
tests.valid = [
979+
...tests.valid,
980+
`
981+
// Valid because functions created with useEvent can be called in a useEffect.
982+
function MyComponent({ theme }) {
983+
const onClick = useEvent(() => {
984+
showNotification(theme);
985+
});
986+
useEffect(() => {
987+
onClick();
988+
});
989+
}
990+
`,
991+
`
992+
// Valid because functions created with useEvent can be called in closures.
993+
function MyComponent({ theme }) {
994+
const onClick = useEvent(() => {
995+
showNotification(theme);
996+
});
997+
return <Child onClick={() => onClick()}></Child>;
998+
}
999+
`,
1000+
`
1001+
// Valid because functions created with useEvent can be called in closures.
1002+
function MyComponent({ theme }) {
1003+
const onClick = useEvent(() => {
1004+
showNotification(theme);
1005+
});
1006+
const onClick2 = () => { onClick() };
1007+
const onClick3 = useCallback(() => onClick(), []);
1008+
return <>
1009+
<Child onClick={onClick2}></Child>
1010+
<Child onClick={onClick3}></Child>
1011+
</>;
1012+
}
1013+
`,
1014+
`
1015+
// Valid because functions created with useEvent can be passed by reference in useEffect
1016+
// and useEvent.
1017+
function MyComponent({ theme }) {
1018+
const onClick = useEvent(() => {
1019+
showNotification(theme);
1020+
});
1021+
const onClick2 = useEvent(() => {
1022+
debounce(onClick);
1023+
});
1024+
useEffect(() => {
1025+
let id = setInterval(onClick, 100);
1026+
return () => clearInterval(onClick);
1027+
}, []);
1028+
return <Child onClick={() => onClick2()} />
1029+
}
1030+
`,
1031+
`
1032+
const MyComponent = ({theme}) => {
1033+
const onClick = useEvent(() => {
1034+
showNotification(theme);
1035+
});
1036+
return <Child onClick={() => onClick()}></Child>;
1037+
};
1038+
`,
1039+
`
1040+
function MyComponent({ theme }) {
1041+
const notificationService = useNotifications();
1042+
const showNotification = useEvent((text) => {
1043+
notificationService.notify(theme, text);
1044+
});
1045+
const onClick = useEvent((text) => {
1046+
showNotification(text);
1047+
});
1048+
return <Child onClick={(text) => onClick(text)} />
1049+
}
1050+
`,
1051+
`
1052+
function MyComponent({ theme }) {
1053+
useEffect(() => {
1054+
onClick();
1055+
});
1056+
const onClick = useEvent(() => {
1057+
showNotification(theme);
1058+
});
1059+
}
1060+
`,
1061+
];
1062+
tests.invalid = [
1063+
...tests.invalid,
10551064
{
10561065
code: `
1057-
function MyComponent({ theme }) {
1058-
const onClick = useEvent(() => {
1059-
showNotification(theme);
1060-
});
1061-
return <Child onClick={onClick}></Child>;
1062-
}
1063-
`,
1066+
function MyComponent({ theme }) {
1067+
const onClick = useEvent(() => {
1068+
showNotification(theme);
1069+
});
1070+
return <Child onClick={onClick}></Child>;
1071+
}
1072+
`,
10641073
errors: [useEventError('onClick')],
10651074
},
10661075
{
10671076
code: `
1068-
// This should error even though it shares an identifier name with the below
1069-
function MyComponent({theme}) {
1070-
const onClick = useEvent(() => {
1071-
showNotification(theme)
1072-
});
1073-
return <Child onClick={onClick} />
1074-
}
1077+
// This should error even though it shares an identifier name with the below
1078+
function MyComponent({theme}) {
1079+
const onClick = useEvent(() => {
1080+
showNotification(theme)
1081+
});
1082+
return <Child onClick={onClick} />
1083+
}
10751084
1076-
// The useEvent function shares an identifier name with the above
1077-
function MyOtherComponent({theme}) {
1078-
const onClick = useEvent(() => {
1079-
showNotification(theme)
1080-
});
1081-
return <Child onClick={() => onClick()} />
1082-
}
1083-
`,
1085+
// The useEvent function shares an identifier name with the above
1086+
function MyOtherComponent({theme}) {
1087+
const onClick = useEvent(() => {
1088+
showNotification(theme)
1089+
});
1090+
return <Child onClick={() => onClick()} />
1091+
}
1092+
`,
10841093
errors: [{...useEventError('onClick'), line: 4}],
10851094
},
10861095
{
10871096
code: `
1088-
const MyComponent = ({ theme }) => {
1089-
const onClick = useEvent(() => {
1090-
showNotification(theme);
1091-
});
1092-
return <Child onClick={onClick}></Child>;
1093-
}
1094-
`,
1097+
const MyComponent = ({ theme }) => {
1098+
const onClick = useEvent(() => {
1099+
showNotification(theme);
1100+
});
1101+
return <Child onClick={onClick}></Child>;
1102+
}
1103+
`,
10951104
errors: [useEventError('onClick')],
10961105
},
10971106
{
10981107
code: `
1099-
// Invalid because onClick is being aliased to foo but not invoked
1100-
function MyComponent({ theme }) {
1101-
const onClick = useEvent(() => {
1102-
showNotification(theme);
1103-
});
1104-
let foo;
1105-
useEffect(() => {
1106-
foo = onClick;
1107-
});
1108-
return <Bar onClick={foo} />
1109-
}
1110-
`,
1108+
// Invalid because onClick is being aliased to foo but not invoked
1109+
function MyComponent({ theme }) {
1110+
const onClick = useEvent(() => {
1111+
showNotification(theme);
1112+
});
1113+
let foo;
1114+
useEffect(() => {
1115+
foo = onClick;
1116+
});
1117+
return <Bar onClick={foo} />
1118+
}
1119+
`,
11111120
errors: [useEventError('onClick')],
11121121
},
1113-
],
1114-
};
1122+
];
1123+
}
11151124

11161125
function conditionalError(hook, hasPreviousFinalizer = false) {
11171126
return {

packages/eslint-plugin-react-hooks/src/ExhaustiveDeps.js

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -225,10 +225,7 @@ export default {
225225
if (name === 'useRef' && id.type === 'Identifier') {
226226
// useRef() return value is stable.
227227
return true;
228-
} else if (
229-
name === 'experimental_useEvent' &&
230-
id.type === 'Identifier'
231-
) {
228+
} else if (isUseEventIdentifier(callee) && id.type === 'Identifier') {
232229
// useEvent() return value is stable.
233230
return true;
234231
} else if (name === 'useState' || name === 'useReducer') {
@@ -1827,3 +1824,10 @@ function isSameIdentifier(a, b) {
18271824
function isAncestorNodeOf(a, b) {
18281825
return a.range[0] <= b.range[0] && a.range[1] >= b.range[1];
18291826
}
1827+
1828+
function isUseEventIdentifier(node) {
1829+
if (__EXPERIMENTAL__) {
1830+
return node.type === 'Identifier' && node.name === 'useEvent';
1831+
}
1832+
return false;
1833+
}

packages/eslint-plugin-react-hooks/src/RulesOfHooks.js

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -101,10 +101,10 @@ function isInsideComponentOrHook(node) {
101101
}
102102

103103
function isUseEventIdentifier(node) {
104-
return (
105-
node.type === 'Identifier' &&
106-
(node.name === 'useEvent' || node.name === 'experimental_useEvent')
107-
);
104+
if (__EXPERIMENTAL__) {
105+
return node.type === 'Identifier' && node.name === 'useEvent';
106+
}
107+
return false;
108108
}
109109

110110
export default {

0 commit comments

Comments
 (0)