Skip to content

Commit 5769b53

Browse files
TildaDaresljharb
authored andcommitted
[Fix] display-name: Accept forwardRef and Memo nesting in newer React versions
1 parent 151a6ed commit 5769b53

File tree

3 files changed

+132
-4
lines changed

3 files changed

+132
-4
lines changed

CHANGELOG.md

+3-1
Original file line numberDiff line numberDiff line change
@@ -10,12 +10,14 @@ This change log adheres to standards from [Keep a CHANGELOG](https://keepachange
1010

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

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

20+
[#3321]: https://github.com/jsx-eslint/eslint-plugin-react/pull/3321
1921
[#3320]: https://github.com/jsx-eslint/eslint-plugin-react/pull/3320
2022
[#3317]: https://github.com/jsx-eslint/eslint-plugin-react/pull/3317
2123
[#3315]: https://github.com/jsx-eslint/eslint-plugin-react/pull/3315

lib/rules/display-name.js

+19
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ const Components = require('../util/Components');
1111
const astUtil = require('../util/ast');
1212
const componentUtil = require('../util/componentUtil');
1313
const docsUrl = require('../util/docsUrl');
14+
const testReactVersion = require('../util/version').testReactVersion;
1415
const propsUtil = require('../util/props');
1516
const report = require('../util/report');
1617

@@ -58,11 +59,29 @@ module.exports = {
5859
});
5960
}
6061

62+
/**
63+
* Checks if React.forwardRef is nested inside React.memo
64+
* @param {ASTNode} node The AST node being checked.
65+
* @returns {Boolean} True if React.forwardRef is nested inside React.memo, false if not.
66+
*/
67+
function isNestedMemo(node) {
68+
const argumentIsCallExpression = node.arguments && node.arguments[0] && node.arguments[0].type === 'CallExpression';
69+
70+
return node.type === 'CallExpression' && argumentIsCallExpression && utils.isPragmaComponentWrapper(node);
71+
}
72+
6173
/**
6274
* Reports missing display name for a given component
6375
* @param {Object} component The component to process
6476
*/
6577
function reportMissingDisplayName(component) {
78+
if (
79+
testReactVersion(context, '^0.14.10 || ^15.7.0 || >= 16.12.0')
80+
&& isNestedMemo(component.node)
81+
) {
82+
return;
83+
}
84+
6685
report(context, messages.noDisplayName, 'noDisplayName', {
6786
node: component.node,
6887
});

tests/lib/rules/display-name.js

+110-3
Original file line numberDiff line numberDiff line change
@@ -601,6 +601,94 @@ ruleTester.run('display-name', rule, {
601601
)
602602
`,
603603
},
604+
{
605+
// Nested React.forwardRef should be accepted in React versions in the following range:
606+
// ^0.14.10 || ^15.7.0 || >= 16.12.0
607+
code: `
608+
import React from 'react'
609+
610+
const MemoizedForwardRefComponentLike = React.memo(
611+
React.forwardRef(function({ world }, ref) {
612+
return <div ref={ref}>Hello {world}</div>
613+
})
614+
)
615+
`,
616+
settings: {
617+
react: {
618+
version: '16.14.0',
619+
},
620+
},
621+
},
622+
{
623+
// Nested React.forwardRef should be accepted in React versions in the following range:
624+
// ^0.14.10 || ^15.7.0 || >= 16.12.0
625+
code: `
626+
import React from 'react'
627+
628+
const MemoizedForwardRefComponentLike = React.memo(
629+
React.forwardRef(({ world }, ref) => {
630+
return <div ref={ref}>Hello {world}</div>
631+
})
632+
)
633+
`,
634+
settings: {
635+
react: {
636+
version: '15.7.0',
637+
},
638+
},
639+
},
640+
{
641+
// Nested React.forwardRef should be accepted in React versions in the following range:
642+
// ^0.14.10 || ^15.7.0 || >= 16.12.0
643+
code: `
644+
import React from 'react'
645+
646+
const MemoizedForwardRefComponentLike = React.memo(
647+
React.forwardRef(function ComponentLike({ world }, ref) {
648+
return <div ref={ref}>Hello {world}</div>
649+
})
650+
)
651+
`,
652+
settings: {
653+
react: {
654+
version: '16.12.1',
655+
},
656+
},
657+
},
658+
{
659+
// Nested React.forwardRef should be accepted in React versions in the following range:
660+
// ^0.14.10 || ^15.7.0 || >= 16.12.0
661+
code: `
662+
export const ComponentWithForwardRef = React.memo(
663+
React.forwardRef(function Component({ world }) {
664+
return <div>Hello {world}</div>
665+
})
666+
)
667+
`,
668+
settings: {
669+
react: {
670+
version: '0.14.11',
671+
},
672+
},
673+
},
674+
{
675+
// Nested React.forwardRef should be accepted in React versions in the following range:
676+
// ^0.14.10 || ^15.7.0 || >= 16.12.0
677+
code: `
678+
import React from 'react'
679+
680+
const MemoizedForwardRefComponentLike = React.memo(
681+
React.forwardRef(function({ world }, ref) {
682+
return <div ref={ref}>Hello {world}</div>
683+
})
684+
)
685+
`,
686+
settings: {
687+
react: {
688+
version: '15.7.1',
689+
},
690+
},
691+
},
604692
]),
605693

606694
invalid: parsers.all([
@@ -823,7 +911,9 @@ ruleTester.run('display-name', rule, {
823911
errors: [{ messageId: 'noDisplayName' }],
824912
},
825913
{
826-
// Only trigger an error for the outer React.memo
914+
// Only trigger an error for the outer React.memo,
915+
// if the React version is not in the following range:
916+
// ^0.14.10 || ^15.7.0 || >= 16.12.0
827917
code: `
828918
import React from 'react'
829919
@@ -837,19 +927,31 @@ ruleTester.run('display-name', rule, {
837927
{
838928
messageId: 'noDisplayName',
839929
}],
930+
settings: {
931+
react: {
932+
version: '15.6.0',
933+
},
934+
},
840935
},
841936
{
842-
// Only trigger an error for the outer React.memo
937+
// Only trigger an error for the outer React.memo,
938+
// if the React version is not in the following range:
939+
// ^0.14.10 || ^15.7.0 || >= ^16.12.0
843940
code: `
844941
import React from 'react'
845942
846943
const MemoizedForwardRefComponentLike = React.memo(
847944
React.forwardRef(function({ world }, ref) {
848945
return <div ref={ref}>Hello {world}</div>
849-
})
946+
})
850947
)
851948
`,
852949
errors: [{ messageId: 'noDisplayName' }],
950+
settings: {
951+
react: {
952+
version: '0.14.2',
953+
},
954+
},
853955
},
854956
{
855957
// React does not handle the result of forwardRef being passed into memo
@@ -865,6 +967,11 @@ ruleTester.run('display-name', rule, {
865967
)
866968
`,
867969
errors: [{ messageId: 'noDisplayName' }],
970+
settings: {
971+
react: {
972+
version: '15.0.1',
973+
},
974+
},
868975
},
869976
{
870977
code: `

0 commit comments

Comments
 (0)