Compare commits

...

1 Commits

Author SHA1 Message Date
Jordan Brown
28a86ffe47 [lint] Allow useEffectEvent in useLayoutEffect and useInsertionEffect 2025-09-15 09:11:08 -04:00
2 changed files with 69 additions and 3 deletions

View File

@@ -1430,6 +1430,72 @@ if (__EXPERIMENTAL__) {
} }
`, `,
}, },
{
code: normalizeIndent`
// Valid because functions created with useEffectEvent can be called in useLayoutEffect.
function MyComponent({ theme }) {
const onClick = useEffectEvent(() => {
showNotification(theme);
});
useLayoutEffect(() => {
onClick();
});
React.useLayoutEffect(() => {
onClick();
});
}
`,
},
{
code: normalizeIndent`
// Valid because functions created with useEffectEvent can be called in useInsertionEffect.
function MyComponent({ theme }) {
const onClick = useEffectEvent(() => {
showNotification(theme);
});
useInsertionEffect(() => {
onClick();
});
React.useInsertionEffect(() => {
onClick();
});
}
`,
},
{
code: normalizeIndent`
// Valid because functions created with useEffectEvent can be passed by reference in useLayoutEffect
// and useInsertionEffect.
function MyComponent({ theme }) {
const onClick = useEffectEvent(() => {
showNotification(theme);
});
const onClick2 = useEffectEvent(() => {
debounce(onClick);
debounce(() => onClick());
debounce(() => { onClick() });
deboucne(() => debounce(onClick));
});
useLayoutEffect(() => {
let id = setInterval(() => onClick(), 100);
return () => clearInterval(onClick);
}, []);
React.useLayoutEffect(() => {
let id = setInterval(() => onClick(), 100);
return () => clearInterval(onClick);
}, []);
useInsertionEffect(() => {
let id = setInterval(() => onClick(), 100);
return () => clearInterval(onClick);
}, []);
React.useInsertionEffect(() => {
let id = setInterval(() => onClick(), 100);
return () => clearInterval(onClick);
}, []);
return null;
}
`,
},
]; ];
allTests.invalid = [ allTests.invalid = [
...allTests.invalid, ...allTests.invalid,

View File

@@ -147,8 +147,8 @@ function getNodeWithoutReactNamespace(
return node; return node;
} }
function isUseEffectIdentifier(node: Node): boolean { function isEffectIdentifier(node: Node): boolean {
return node.type === 'Identifier' && node.name === 'useEffect'; return node.type === 'Identifier' && (node.name === 'useEffect' || node.name === 'useLayoutEffect' || node.name === 'useInsertionEffect');
} }
function isUseEffectEventIdentifier(node: Node): boolean { function isUseEffectEventIdentifier(node: Node): boolean {
if (__EXPERIMENTAL__) { if (__EXPERIMENTAL__) {
@@ -726,7 +726,7 @@ const rule = {
// Check all `useEffect` and `React.useEffect`, `useEffectEvent`, and `React.useEffectEvent` // Check all `useEffect` and `React.useEffect`, `useEffectEvent`, and `React.useEffectEvent`
const nodeWithoutNamespace = getNodeWithoutReactNamespace(node.callee); const nodeWithoutNamespace = getNodeWithoutReactNamespace(node.callee);
if ( if (
(isUseEffectIdentifier(nodeWithoutNamespace) || (isEffectIdentifier(nodeWithoutNamespace) ||
isUseEffectEventIdentifier(nodeWithoutNamespace)) && isUseEffectEventIdentifier(nodeWithoutNamespace)) &&
node.arguments.length > 0 node.arguments.length > 0
) { ) {