Skip to content
Open
2 changes: 1 addition & 1 deletion azure-pipelines.yml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
pool:
vmImage: 'Ubuntu-16.04'
vmImage: 'ubuntu-18.04'
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for fixing that in #31! I'd appreciate if you rebased this PR on the latest master to deduplicate the commit that changes this value

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see this change is still in "new" in this PR, but it was already merged to master. Could you rebase and drop the commits that are already on master?


steps:
- task: NodeTool@0
Expand Down
1 change: 1 addition & 0 deletions src/react-hooks-nesting-walker/error-messages.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,4 +17,5 @@ export const ERROR_MESSAGES = {
'A hook cannot be used inside of another function',
invalidFunctionExpression: 'A hook cannot be used inside of another function',
hookAfterEarlyReturn: 'A hook should not appear after a return statement',
anonymousFunctionIllegalCallback: `Hook is in an anonymous function that is passed to an illegal callback. Legal callbacks identifiers that can receive anonymous functions as arguments are memo and forwardRef`,
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looking at this error message, I am afraid it may be confusing for regular developers who were not working with ASTs/language parsing before.

Suggested change
anonymousFunctionIllegalCallback: `Hook is in an anonymous function that is passed to an illegal callback. Legal callbacks identifiers that can receive anonymous functions as arguments are memo and forwardRef`,
anonymousFunctionIllegalCallback: `Hook is used in an anonymous function that is provided as an argument in an unexpected function. Functions that can receive anonymous functions with hook calls are "React.memo" and "React.forwardRef". If this is intended, add a name to the anonymous wrapping function to make it appear as a component.`,

};
11 changes: 11 additions & 0 deletions src/react-hooks-nesting-walker/react-hooks-nesting-walker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -190,6 +190,17 @@ export class ReactHooksNestingWalker extends RuleWalker {
return;
}

/**
* Detect if the unnamed expression is wrapped in a illegal function call
*/
if (isIdentifier((ancestor.parent as CallExpression)?.expression)) {
this.addFailureAtNode(
hookNode,
ERROR_MESSAGES.anonymousFunctionIllegalCallback,
);
return;
}

// Disallow using hooks inside other kinds of functions
this.addFailureAtNode(hookNode, ERROR_MESSAGES.invalidFunctionExpression);
return;
Expand Down
26 changes: 26 additions & 0 deletions test/tslint-rule/anonymous-function-error.ts.lint
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
const IllegalComponent = wrapper(function() {
useEffect(() => {});
~~~~~~~~~~~~~~~~~~~ [Hook is in an anonymous function that is passed to an illegal callback. Legal callbacks identifiers that can receive anonymous functions as arguments are memo and forwardRef]
})

const LegalAnonymousComponent = function() {
useEffect(() => {});
}

const ForwardedComponent = React.forwardRef(function(props, ref) {
useEffect(() => {
console.log("I am legal")
});
})

const MemoizedComponent = React.memo((props) => {
const [state, setState] = React.useState(props.initValue);
return <span>{state}</span>
})

const Functor = function() {
const cb = React.useCallback(() => {
const r = React.useRef()
~~~~~~~~~~~~~~ [A hook cannot be used inside of another function]
}, [])
}