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] display-name: Accept forwardRef and Memo nesting in newer React versions #3321

Merged
merged 1 commit into from
Jul 6, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,14 @@ This change log adheres to standards from [Keep a CHANGELOG](https://keepachange

### Fixed
* [`jsx-no-literals`]: properly error on children with noAttributeStrings: true ([#3317][] @TildaDares)
* [`jsx-key`]: catch key errors inside conditional statements ([#3320][] @TildaDates)
* [`jsx-key`]: catch key errors inside conditional statements ([#3320][] @TildaDares)
* [`display-name`]: Accept forwardRef and Memo nesting in newer React versions ([#3321][] @TildaDares)

### Changed
* [Refactor] [`jsx-indent-props`]: improved readability of the checkNodesIndent function ([#3315][] @caroline223)
* [Tests] [`jsx-indent`], [`jsx-one-expression-per-line`]: add passing test cases ([#3314][] @ROSSROSALES)

[#3321]: https://github.com/jsx-eslint/eslint-plugin-react/pull/3321
[#3320]: https://github.com/jsx-eslint/eslint-plugin-react/pull/3320
[#3317]: https://github.com/jsx-eslint/eslint-plugin-react/pull/3317
[#3315]: https://github.com/jsx-eslint/eslint-plugin-react/pull/3315
Expand Down
19 changes: 19 additions & 0 deletions lib/rules/display-name.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ const Components = require('../util/Components');
const astUtil = require('../util/ast');
const componentUtil = require('../util/componentUtil');
const docsUrl = require('../util/docsUrl');
const testReactVersion = require('../util/version').testReactVersion;
const propsUtil = require('../util/props');
const report = require('../util/report');

Expand Down Expand Up @@ -58,11 +59,29 @@ module.exports = {
});
}

/**
* Checks if React.forwardRef is nested inside React.memo
* @param {ASTNode} node The AST node being checked.
* @returns {Boolean} True if React.forwardRef is nested inside React.memo, false if not.
*/
function isNestedMemo(node) {
const argumentIsCallExpression = node.arguments && node.arguments[0] && node.arguments[0].type === 'CallExpression';

return node.type === 'CallExpression' && argumentIsCallExpression && utils.isPragmaComponentWrapper(node);
}

/**
* Reports missing display name for a given component
* @param {Object} component The component to process
*/
function reportMissingDisplayName(component) {
if (
testReactVersion(context, '^0.14.10 || ^15.7.0 || >= 16.12.0')
&& isNestedMemo(component.node)
) {
return;
}

report(context, messages.noDisplayName, 'noDisplayName', {
node: component.node,
});
Expand Down
113 changes: 110 additions & 3 deletions tests/lib/rules/display-name.js
Original file line number Diff line number Diff line change
Expand Up @@ -601,6 +601,94 @@ ruleTester.run('display-name', rule, {
)
`,
},
{
// Nested React.forwardRef should be accepted in React versions in the following range:
// ^0.14.10 || ^15.7.0 || >= 16.12.0
code: `
import React from 'react'

const MemoizedForwardRefComponentLike = React.memo(
React.forwardRef(function({ world }, ref) {
return <div ref={ref}>Hello {world}</div>
})
)
`,
settings: {
react: {
version: '16.14.0',
},
},
},
{
// Nested React.forwardRef should be accepted in React versions in the following range:
// ^0.14.10 || ^15.7.0 || >= 16.12.0
code: `
import React from 'react'

const MemoizedForwardRefComponentLike = React.memo(
React.forwardRef(({ world }, ref) => {
return <div ref={ref}>Hello {world}</div>
})
)
`,
settings: {
react: {
version: '15.7.0',
},
},
},
{
// Nested React.forwardRef should be accepted in React versions in the following range:
// ^0.14.10 || ^15.7.0 || >= 16.12.0
code: `
import React from 'react'

const MemoizedForwardRefComponentLike = React.memo(
React.forwardRef(function ComponentLike({ world }, ref) {
return <div ref={ref}>Hello {world}</div>
})
)
`,
settings: {
react: {
version: '16.12.1',
},
},
},
{
// Nested React.forwardRef should be accepted in React versions in the following range:
// ^0.14.10 || ^15.7.0 || >= 16.12.0
code: `
export const ComponentWithForwardRef = React.memo(
React.forwardRef(function Component({ world }) {
return <div>Hello {world}</div>
})
)
`,
settings: {
react: {
version: '0.14.11',
},
},
},
{
// Nested React.forwardRef should be accepted in React versions in the following range:
// ^0.14.10 || ^15.7.0 || >= 16.12.0
code: `
import React from 'react'

const MemoizedForwardRefComponentLike = React.memo(
React.forwardRef(function({ world }, ref) {
return <div ref={ref}>Hello {world}</div>
})
)
`,
settings: {
react: {
version: '15.7.1',
},
},
},
]),

invalid: parsers.all([
Expand Down Expand Up @@ -823,7 +911,9 @@ ruleTester.run('display-name', rule, {
errors: [{ messageId: 'noDisplayName' }],
},
{
// Only trigger an error for the outer React.memo
// Only trigger an error for the outer React.memo,
// if the React version is not in the following range:
// ^0.14.10 || ^15.7.0 || >= 16.12.0
code: `
import React from 'react'

Expand All @@ -837,19 +927,31 @@ ruleTester.run('display-name', rule, {
{
messageId: 'noDisplayName',
}],
settings: {
react: {
version: '15.6.0',
},
},
},
{
// Only trigger an error for the outer React.memo
// Only trigger an error for the outer React.memo,
// if the React version is not in the following range:
// ^0.14.10 || ^15.7.0 || >= ^16.12.0
code: `
import React from 'react'

const MemoizedForwardRefComponentLike = React.memo(
React.forwardRef(function({ world }, ref) {
return <div ref={ref}>Hello {world}</div>
})
})
)
`,
errors: [{ messageId: 'noDisplayName' }],
settings: {
react: {
version: '0.14.2',
},
},
},
{
// React does not handle the result of forwardRef being passed into memo
Expand All @@ -865,6 +967,11 @@ ruleTester.run('display-name', rule, {
)
`,
errors: [{ messageId: 'noDisplayName' }],
settings: {
react: {
version: '15.0.1',
},
},
},
{
code: `
Expand Down