@samitouri / QOS-React-2 / commits / b0533fe33c

fix(eslint-plugin-react-hooks): Support optional chaining when accessing prototype method inside useCallback and useMemo #19061 (#19062)

* fix(eslint-plugin-react-hooks): Support optional chaining when accessing prototype method #19061 * run prettier * Add fix for #19043

Fred Vollmer committed Jun 30, 2020 at 07:14 UTC b0533fe33cbbd2c07f11bf3a1e97494a73698016
2 files changed +52 -2
packages/eslint-plugin-react-hooks/__tests__/ESLintRuleExhaustiveDeps-test.js
+47
@@ -289,6 +289,24 @@ const tests = {
289 }
290 `,
291 },
292 + {
293 + code: normalizeIndent`
294 + function MyComponent(props) {
295 + useMemo(() => {
296 + console.log(props.foo?.toString());
297 + }, [props.foo]);
298 + }
299 + `,
300 + },
301 + {
302 + code: normalizeIndent`
303 + function MyComponent(props) {
304 + useCallback(() => {
305 + console.log(props.foo?.toString());
306 + }, [props.foo]);
307 + }
308 + `,
309 + },
310 {
311 code: normalizeIndent`
312 function MyComponent() {
@@ -1235,6 +1253,35 @@ const tests = {
1253 },
1254 ],
1255 invalid: [
1256 + {
1257 + code: normalizeIndent`
1258 + function MyComponent(props) {
1259 + useCallback(() => {
1260 + console.log(props.foo?.toString());
1261 + }, []);
1262 + }
1263 + `,
1264 + errors: [
1265 + {
1266 + message:
1267 + "React Hook useCallback has a missing dependency: 'props.foo?.toString'. " +
1268 + 'Either include it or remove the dependency array.',
1269 + suggestions: [
1270 + {
1271 + desc:
1272 + 'Update the dependencies array to be: [props.foo?.toString]',
1273 + output: normalizeIndent`
1274 + function MyComponent(props) {
1275 + useCallback(() => {
1276 + console.log(props.foo?.toString());
1277 + }, [props.foo?.toString]);
1278 + }
1279 + `,
1280 + },
1281 + ],
1282 + },
1283 + ],
1284 + },
1285 {
1286 code: normalizeIndent`
1287 function MyComponent() {
packages/eslint-plugin-react-hooks/src/ExhaustiveDeps.js
+5 -2
@@ -1287,10 +1287,13 @@ function collectRecommendations({
1287 function scanTreeRecursively(node, missingPaths, satisfyingPaths, keyToPath) {
1288 node.children.forEach((child, key) => {
1289 const path = keyToPath(key);
1290 + // For analyzing dependencies, we want the "normalized" path, without any optional chaining ("?.") operator
1291 + // foo?.bar -> foo.bar
1292 + const normalizedPath = path.replace(/\?$/, '');
1293 if (child.isSatisfiedRecursively) {
1294 if (child.hasRequiredNodesBelow) {
1295 // Remember this dep actually satisfied something.
1293 - satisfyingPaths.add(path);
1296 + satisfyingPaths.add(normalizedPath);
1297 }
1298 // It doesn't matter if there's something deeper.
1299 // It would be transitively satisfied since we assume immutability.
@@ -1299,7 +1302,7 @@ function collectRecommendations({
1302 }
1303 if (child.isRequired) {
1304 // Remember that no declared deps satisfied this node.
1302 - missingPaths.add(path);
1305 + missingPaths.add(normalizedPath);
1306 // If we got here, nothing in its subtree was satisfied.
1307 // No need to search further.
1308 return;