Skip to content

Commit

Permalink
[fix] display-name: Fix false positives
Browse files Browse the repository at this point in the history
Wrapping a named function declaration with a React.memo or
React.forwardRef will no longer throw an false positive error

Fixes #2324. Fixes #2269.
  • Loading branch information
BPScott authored and ljharb committed Sep 3, 2019
1 parent c6521ad commit 4a05fa2
Show file tree
Hide file tree
Showing 2 changed files with 131 additions and 2 deletions.
29 changes: 27 additions & 2 deletions lib/rules/display-name.js
Expand Up @@ -90,13 +90,13 @@ module.exports = {
const namedClass = (
(node.type === 'ClassDeclaration' || node.type === 'ClassExpression') &&
node.id &&
node.id.name
!!node.id.name
);

const namedFunctionDeclaration = (
(node.type === 'FunctionDeclaration' || node.type === 'FunctionExpression') &&
node.id &&
node.id.name
!!node.id.name
);

const namedFunctionExpression = (
Expand Down Expand Up @@ -202,6 +202,31 @@ module.exports = {
markDisplayNameAsDeclared(node);
},

CallExpression(node) {
if (!utils.isPragmaComponentWrapper(node)) {
return;
}

if (node.arguments.length > 0 && astUtil.isFunctionLikeExpression(node.arguments[0])) {
// Skip over React.forwardRef declarations that are embeded within
// a React.memo i.e. React.memo(React.forwardRef(/* ... */))
// This means that we raise a single error for the call to React.memo
// instead of one for React.memo and one for React.forwardRef
const isWrappedInAnotherPragma = utils.getPragmaComponentWrapper(node);

if (
!isWrappedInAnotherPragma &&
(ignoreTranspilerName || !hasTranspilerName(node.arguments[0]))
) {
return;
}

if (components.get(node)) {
markDisplayNameAsDeclared(node);
}
}
},

'Program:exit': function () {
const list = components.list();
// Report missing display name for all components
Expand Down
104 changes: 104 additions & 0 deletions tests/lib/rules/display-name.js
Expand Up @@ -455,6 +455,22 @@ ruleTester.run('display-name', rule, {
export default React.memo(Component)
`
}, {
code: `
import React from 'react'
const ComponentWithMemo = React.memo(function Component({ world }) {
return <div>Hello {world}</div>
})
`
}, {
code: `
import React from 'react'
const ForwardRefComponentLike = React.forwardRef(function ComponentLike({ world }, ref) {
return <div ref={ref}>Hello {world}</div>
})
`
}, {
code: `
function F() {
Expand Down Expand Up @@ -684,6 +700,94 @@ ruleTester.run('display-name', rule, {
errors: [{
message: 'Component definition is missing display name'
}]
}, {
code: `
import React from 'react'
const ComponentWithMemo = React.memo(({ world }) => {
return <div>Hello {world}</div>
})
`,
errors: [{
message: 'Component definition is missing display name'
}]
}, {
code: `
import React from 'react'
const ComponentWithMemo = React.memo(function() {
return <div>Hello {world}</div>
})
`,
errors: [{
message: 'Component definition is missing display name'
}]
}, {
code: `
import React from 'react'
const ForwardRefComponentLike = React.forwardRef(({ world }, ref) => {
return <div ref={ref}>Hello {world}</div>
})
`,
errors: [{
message: 'Component definition is missing display name'
}]
}, {
code: `
import React from 'react'
const ForwardRefComponentLike = React.forwardRef(function({ world }, ref) {
return <div ref={ref}>Hello {world}</div>
})
`,
errors: [{
message: 'Component definition is missing display name'
}]
}, {
// Only trigger an error for the outer React.memo
code: `
import React from 'react'
const MemoizedForwardRefComponentLike = React.memo(
React.forwardRef(({ world }, ref) => {
return <div ref={ref}>Hello {world}</div>
})
)
`,
errors: [{
message: 'Component definition is missing display name'
}]
}, {
// Only trigger an error for the outer React.memo
code: `
import React from 'react'
const MemoizedForwardRefComponentLike = React.memo(
React.forwardRef(function({ world }, ref) {
return <div ref={ref}>Hello {world}</div>
})
)
`,
errors: [{
message: 'Component definition is missing display name'
}]
}, {
// React does not handle the result of forwardRef being passed into memo
// ComponentWithMemoAndForwardRef gets shown as Memo(Anonymous)
// See https://github.com/facebook/react/issues/16722
code: `
import React from 'react'
const MemoizedForwardRefComponentLike = React.memo(
React.forwardRef(function ComponentLike({ world }, ref) {
return <div ref={ref}>Hello {world}</div>
})
)
`,
errors: [{
message: 'Component definition is missing display name'
}]
}, {
code: `
import React from "react";
Expand Down

0 comments on commit 4a05fa2

Please sign in to comment.