Skip to content
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

Fix ExhaustiveDeps ESLint rule throwing with optional chaining #19260

Merged
merged 1 commit into from
Jul 6, 2020

Conversation

lencioni
Copy link
Contributor

@lencioni lencioni commented Jul 6, 2020

Summary

Certain code patterns using optional chaining syntax causes
eslint-plugin-react-hooks to throw an error.

We can avoid the throw by adding some guards. I didn't read through the
code to understand how it works, I just added a guard to every place
where it threw, so maybe there is a better fix closer to the root cause
than what I have here.

In my test case, I noticed that the optional chaining that was used in
the code was not included in the suggestions description or output,
but it seems like it should be. This might make a nice future
improvement on top of this fix, so I left a TODO comment to that effect.

Fixes #19243

Test Plan

yarn test --watch eslint-plugin

Certain code patterns using optional chaining syntax causes
eslint-plugin-react-hooks to throw an error.

We can avoid the throw by adding some guards. I didn't read through the
code to understand how it works, I just added a guard to every place
where it threw, so maybe there is a better fix closer to the root cause
than what I have here.

In my test case, I noticed that the optional chaining that was used in
the code was not included in the suggestions description or output,
but it seems like it should be. This might make a nice future
improvement on top of this fix, so I left a TODO comment to that effect.

Fixes facebook#19243
@codesandbox-ci
Copy link

codesandbox-ci bot commented Jul 6, 2020

This pull request is automatically built and testable in CodeSandbox.

To see build info of the built libraries, click here or the icon next to each commit SHA.

Latest deployment of this branch, based on commit 44b93b6:

Sandbox Source
React Configuration

@sizebot
Copy link

sizebot commented Jul 6, 2020

No significant bundle size changes to report.

Size changes (stable)

Generated by 🚫 dangerJS against 44b93b6

@sizebot
Copy link

sizebot commented Jul 6, 2020

No significant bundle size changes to report.

Size changes (experimental)

Generated by 🚫 dangerJS against 44b93b6

@gaearon
Copy link
Collaborator

gaearon commented Jul 6, 2020

I didn't read through the code to understand how it works, I just added a guard to every place where it threw, so maybe there is a better fix closer to the root cause than what I have here.

I think at the very least we should look at why those items weren't present in those Maps. Perhaps the key is wrong? Then we should fix the key.

@lencioni
Copy link
Contributor Author

lencioni commented Jul 6, 2020

It looks like there is a mismatch between the keys in the dependencies Map and the keys in missingDependencies. The Map includes the optional chaining in its keys (e.g. pizza?.toppings) while the missingDependencies array does not (e.g. pizza.toppings).

@gaearon
Copy link
Collaborator

gaearon commented Jul 6, 2020

Can we think of a fix that would address the root of the problem?

@lencioni
Copy link
Contributor Author

lencioni commented Jul 6, 2020

Yep, looking into that now. In b0533fe it looks like the missingDependencies is normalized to remove the ? bits, so I suppose we could do the same for the dependencies Map keys, which I think would just mean using the normalizedPath instead of path in scanTreeRecursively.

Oh wait, nevermind that's a different code path. I think that Map must be built somewhere else.

@lencioni
Copy link
Contributor Author

lencioni commented Jul 6, 2020

I've poked around a bit and I'm not sure what the best solution is yet. One way to fix this would be to normalize the paths in the dependencies Map like this maybe:

--- a/packages/eslint-plugin-react-hooks/src/ExhaustiveDeps.js
+++ b/packages/eslint-plugin-react-hooks/src/ExhaustiveDeps.js
@@ -561,7 +561,7 @@ export default {
             const isStatic =
               memoizedIsStaticKnownHookValue(resolved) ||
               memoizedIsFunctionWithoutCapturedValues(resolved);
-            dependencies.set(dependency, {
+            dependencies.set(dependency.replace(/\?/g, ''), {
               isStatic,
               references: [reference],
             });

but the downside is the error messages and suggestions will drop the optional chaining, which is not ideal.

I started to investigate fixing it on the other end (so that the missingDependencies Set includes the optional chaining) but that train ended up getting deeper than I have time to investigate at the moment (missingDependencies is built from depTree which comes from createDepTree which ends up normalizing the paths somehow--I think possibly by getChildByKey).

I can modify this PR to normalize out the optional chaining, or leave it as is. Anything more involved I'll have to set this aside for a while. Or perhaps @yanneves or @fredvollmer can offer some better insights, since it seems they've also been in this part of the codebase recently?

Copy link
Contributor

@bvaughn bvaughn left a comment

Choose a reason for hiding this comment

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

Thanks!👍

@bvaughn bvaughn merged commit 0f84b0f into facebook:master Jul 6, 2020
@gaearon
Copy link
Collaborator

gaearon commented Jul 6, 2020

@bvaughn Any particular reason you merged this fix? It doesn't address the root of the problem and I'm worried we'll just get more confusing reports later on.

@bvaughn
Copy link
Contributor

bvaughn commented Jul 7, 2020

Wow. I feel like a clown. I did not see any of the above running discussion between the two of you. I'm not sure how I overlooked it but I somehow did.

I was looking through recent issues and PRs (because of oncall)- saw this, tested it, thought it looked reasonable, so I merged it.

I am sorry!

gaearon added a commit to gaearon/react that referenced this pull request Jul 7, 2020
@gaearon
Copy link
Collaborator

gaearon commented Jul 7, 2020

#19273

gaearon added a commit that referenced this pull request Jul 7, 2020
…9273)

* Revert "Fix ExhaustiveDeps ESLint rule throwing with optional chaining (#19260)"

This reverts commit 0f84b0f.

* Re-add a test from #19260

* Remove all code for optional chaining support

* Consistently treat optional chaining as regular chaining

This is not ideal because our suggestions use normal chaining. But it gets rid of all current edge cases.

* Add more tests

* More consistency in treating normal and optional expressions

* Add regression tests for every occurrence of Optional*
trueadm pushed a commit to trueadm/react that referenced this pull request Jul 8, 2020
…cebook#19273)

* Revert "Fix ExhaustiveDeps ESLint rule throwing with optional chaining (facebook#19260)"

This reverts commit 0f84b0f.

* Re-add a test from facebook#19260

* Remove all code for optional chaining support

* Consistently treat optional chaining as regular chaining

This is not ideal because our suggestions use normal chaining. But it gets rid of all current edge cases.

* Add more tests

* More consistency in treating normal and optional expressions

* Add regression tests for every occurrence of Optional*
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Bug: Cannot read property 'references' of undefined in eslint-plugin-react-hooks v4.0.5
5 participants