-
Notifications
You must be signed in to change notification settings - Fork 660
fix(rome_js_analyze): fix const dependency in react hooks #3508
Conversation
✅ Deploy Preview for rometools canceled.
|
|
||
pub fn is_constant(expr: &JsAnyExpression) -> bool { | ||
for node in expr.syntax().descendants() { | ||
if matches!(node.kind(), JsSyntaxKind::JS_REFERENCE_IDENTIFIER) { |
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.
I like the simplicity of this but I do think it's too limited:
- JSX gets transformed to a createElement call that, to my knowledge, returns a new object reference
- You need to handle
JsxNames
:const Comp = a ? A : B; <Comp />
- are
FunctionExpression
andClassExpression
const? Because they create a new instance every time the variable statement is executed. Same is true forObject
orArrayExpressions
.
This question show that it is important to document the semantics of is_const
. You may further consider to move this function into the specific lint rule that uses it where it's is possible to make more assumptions about the code.
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.
For JSX elements I think they can safely be considered "constant" if they only refer to other constants, at least Babel has the @babel/plugin-transform-react-constant-elements
plugin to hoist these expressions out of the component render function.
For the rest while it's very important that we avoid false positives it does seem like such a simple analysis would also have a bunch of false negatives, for instance I think it would return false
for const plusOne = x => x + 1
when that function could be considered constant at least for the purpose of checking React Hooks
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.
I agree that needs documentation, but I would leave this at the semantic model for two reasons:
- So we have at some point a function we can use in multiple places to avoid different interpretation what is a constants;
- In the future I would like to make this check transient and accept references to constant variables.
I incremented the summary with more possible interpretations what a constant is.
Summary
This PR extends support of useExhaustiveDependencies to consider constant captures as not needed in the dependency list.
What is a constant?
The current implementation assumes that every expression without a reference is a constant.
This bring us some polemic cases:
These are all considered constants, even though
I think this is the case because JS decided to test them by reference and not by value. There are libs specially made to implement equality by value, for example: https://www.npmjs.com/package/are-equal and any assert library. I would say that equality by value is stronger than equality by reference when considering constantness.
Which makes me say that
The same cannot be said by its reference, which is implementation detail.
how eslint deal with constantness
eslint has some more complex cases for constantness that we can incorporate later.
https://eslint.org/docs/latest/rules/no-constant-condition
https://eslint.org/docs/latest/rules/no-constant-binary-expression
Test Plan