-
Notifications
You must be signed in to change notification settings - Fork 10
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Adds illegal anonymous function error msg #30
base: master
Are you sure you want to change the base?
Changes from all commits
d17deb8
b75e46c
e41cd6e
e8f1a5a
9108ec2
39cd396
b2d11ae
7e4048f
e8a4a70
eca2ac7
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,5 +1,5 @@ | ||
pool: | ||
vmImage: 'Ubuntu-16.04' | ||
vmImage: 'ubuntu-18.04' | ||
|
||
steps: | ||
- task: NodeTool@0 | ||
|
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
|
@@ -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', | ||||||
ruiconti marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||
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`, | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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
|
||||||
}; |
Original file line number | Diff line number | Diff line change | ||||||
---|---|---|---|---|---|---|---|---|
|
@@ -190,6 +190,21 @@ export class ReactHooksNestingWalker extends RuleWalker { | |||||||
return; | ||||||||
} | ||||||||
|
||||||||
/** | ||||||||
* Detect if the unnamed expression is wrapped in a illegal function call | ||||||||
*/ | ||||||||
if ( | ||||||||
isCallExpression(ancestor.parent) && | ||||||||
isIdentifier(ancestor.parent.expression) && | ||||||||
!isComponentOrHookIdentifier(ancestor.parent.expression) | ||||||||
Comment on lines
+198
to
+199
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This mostly looks good now 👍 One change that I believe is worth it, is using const IllegalComponent = Wrapper(function() {
useEffect(() => {});
})
It should also work in this case, where the current code uses the more-generic message:
I would expect to see the When using
Suggested change
(note that I removed the
However, that also causes changes in other tests: // Usage inside other functions
[].forEach(() => {
useEffect(() => {});
- ~~~~~~~~~~~~~~~~~~~ [A hook cannot be used inside of another function]
+ ~~~~~~~~~~~~~~~~~~~ [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 wrapping function to make it appear as a component.]
}) I believe that is acceptable, but not sure if it's worth it - the new error message (either the one you suggested or the one from me) can mislead the engineer. Overall, it seems like a hard problem to detect function calls and differentiate between situations when the user is using a hook in a component-like function, or a regular callback (e.g. I'm not sure how you want to proceed here. The current implementation adds this new type of error only in very special scenarios (only when the called function's name does not look like a hook/component, so uppercase function names are ignored) and does not detect all cases, even though the implementation suggests it does. We could modify the implementation to say that it detects some invalid function calls, or we could implement Personally, I would rather implement a more robust solution to try to make the error messages more precise |
||||||||
) { | ||||||||
this.addFailureAtNode( | ||||||||
hookNode, | ||||||||
ERROR_MESSAGES.anonymousFunctionIllegalCallback, | ||||||||
); | ||||||||
return; | ||||||||
} | ||||||||
|
||||||||
// Disallow using hooks inside other kinds of functions | ||||||||
this.addFailureAtNode(hookNode, ERROR_MESSAGES.invalidFunctionExpression); | ||||||||
return; | ||||||||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
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(() => { | ||
Gelio marked this conversation as resolved.
Show resolved
Hide resolved
|
||
const r = React.useRef() | ||
~~~~~~~~~~~~~~ [A hook cannot be used inside of another function] | ||
}, []) | ||
|
||
const cb = useCallback(() => { | ||
const r = React.useRef() | ||
~~~~~~~~~~~~~~ [A hook cannot be used inside of another function] | ||
}, []) | ||
|
||
obj[(props) => { useRef() }] = 123; | ||
~~~~~~~~ [A hook cannot be used inside of another function] | ||
|
||
new Abc((props) => { useRef() }) | ||
~~~~~~~~ [A hook cannot be used inside of another function] | ||
} |
There was a problem hiding this comment.
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 valueThere was a problem hiding this comment.
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 onmaster
?