mirror of
https://github.com/facebook/react.git
synced 2025-11-01 09:12:30 +00:00
[lint] Enable custom hooks configuration for useEffectEvent calling rules
This commit is contained in:
@@ -581,6 +581,27 @@ const allTests = {
|
||||
};
|
||||
`,
|
||||
},
|
||||
{
|
||||
code: normalizeIndent`
|
||||
// Valid: useEffectEvent can be called in custom effect hooks configured via ESLint settings
|
||||
function MyComponent({ theme }) {
|
||||
const onClick = useEffectEvent(() => {
|
||||
showNotification(theme);
|
||||
});
|
||||
useMyEffect(() => {
|
||||
onClick();
|
||||
});
|
||||
useServerEffect(() => {
|
||||
onClick();
|
||||
});
|
||||
}
|
||||
`,
|
||||
settings: {
|
||||
'react-eslint': {
|
||||
additionalEffectHooks: '(useMyEffect|useServerEffect)',
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
invalid: [
|
||||
{
|
||||
@@ -1353,6 +1374,39 @@ const allTests = {
|
||||
`,
|
||||
errors: [tryCatchUseError('use')],
|
||||
},
|
||||
{
|
||||
code: normalizeIndent`
|
||||
// Invalid: useEffectEvent should not be callable in regular custom hooks without additional configuration
|
||||
function MyComponent({ theme }) {
|
||||
const onClick = useEffectEvent(() => {
|
||||
showNotification(theme);
|
||||
});
|
||||
useCustomHook(() => {
|
||||
onClick();
|
||||
});
|
||||
}
|
||||
`,
|
||||
errors: [useEffectEventError('onClick', true)],
|
||||
},
|
||||
{
|
||||
code: normalizeIndent`
|
||||
// Invalid: useEffectEvent should not be callable in hooks not matching the settings regex
|
||||
function MyComponent({ theme }) {
|
||||
const onClick = useEffectEvent(() => {
|
||||
showNotification(theme);
|
||||
});
|
||||
useWrongHook(() => {
|
||||
onClick();
|
||||
});
|
||||
}
|
||||
`,
|
||||
settings: {
|
||||
'react-eslint': {
|
||||
additionalEffectHooks: 'useMyEffect',
|
||||
},
|
||||
},
|
||||
errors: [useEffectEventError('onClick', true)],
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
|
||||
@@ -147,8 +147,23 @@ function getNodeWithoutReactNamespace(
|
||||
return node;
|
||||
}
|
||||
|
||||
function isEffectIdentifier(node: Node): boolean {
|
||||
return node.type === 'Identifier' && (node.name === 'useEffect' || node.name === 'useLayoutEffect' || node.name === 'useInsertionEffect');
|
||||
function isEffectIdentifier(node: Node, additionalHooks?: RegExp): boolean {
|
||||
const isBuiltInEffect =
|
||||
node.type === 'Identifier' &&
|
||||
(node.name === 'useEffect' ||
|
||||
node.name === 'useLayoutEffect' ||
|
||||
node.name === 'useInsertionEffect');
|
||||
|
||||
if (isBuiltInEffect) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Check if this matches additional hooks configured by the user
|
||||
if (additionalHooks && node.type === 'Identifier') {
|
||||
return additionalHooks.test(node.name);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
function isUseEffectEventIdentifier(node: Node): boolean {
|
||||
if (__EXPERIMENTAL__) {
|
||||
@@ -169,8 +184,26 @@ const rule = {
|
||||
recommended: true,
|
||||
url: 'https://react.dev/reference/rules/rules-of-hooks',
|
||||
},
|
||||
schema: [
|
||||
{
|
||||
type: 'object',
|
||||
additionalProperties: false,
|
||||
properties: {
|
||||
additionalHooks: {
|
||||
type: 'string',
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
create(context: Rule.RuleContext) {
|
||||
const settings = context.settings || {};
|
||||
|
||||
const additionalEffectHooks =
|
||||
settings['react-eslint'] && settings['react-eslint'].additionalEffectHooks
|
||||
? new RegExp(settings['react-eslint'].additionalEffectHooks)
|
||||
: undefined;
|
||||
|
||||
let lastEffect: CallExpression | null = null;
|
||||
const codePathReactHooksMapStack: Array<
|
||||
Map<Rule.CodePathSegment, Array<Node>>
|
||||
@@ -726,7 +759,7 @@ const rule = {
|
||||
// Check all `useEffect` and `React.useEffect`, `useEffectEvent`, and `React.useEffectEvent`
|
||||
const nodeWithoutNamespace = getNodeWithoutReactNamespace(node.callee);
|
||||
if (
|
||||
(isEffectIdentifier(nodeWithoutNamespace) ||
|
||||
(isEffectIdentifier(nodeWithoutNamespace, additionalEffectHooks) ||
|
||||
isUseEffectEventIdentifier(nodeWithoutNamespace)) &&
|
||||
node.arguments.length > 0
|
||||
) {
|
||||
|
||||
Reference in New Issue
Block a user