diff --git a/CHANGELOG.md b/CHANGELOG.md
index 35bb82625a..e9da7d044f 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -10,9 +10,11 @@ This change log adheres to standards from [Keep a CHANGELOG](https://keepachange
* add type generation ([#3830][] @voxpelli)
* [`no-unescaped-entities`]: add suggestions ([#3831][] @StyleShit)
* [`forbid-component-props`]: add `allowedForPatterns`/`disallowedForPatterns` options ([#3805][] @Efimenko)
+* [`no-unstable-nested-components`]: add `propNamePattern` to support custom render prop naming conventions ([#3826][] @danreeves)
[#3831]: https://github.com/jsx-eslint/eslint-plugin-react/pull/3831
[#3830]: https://github.com/jsx-eslint/eslint-plugin-react/pull/3830
+[#3826]: https://github.com/jsx-eslint/eslint-plugin-react/pull/3826
[#3805]: https://github.com/jsx-eslint/eslint-plugin-react/pull/3805
## [7.36.1] - 2024.09.12
diff --git a/docs/rules/no-unstable-nested-components.md b/docs/rules/no-unstable-nested-components.md
index 8117636805..45fa214ef4 100644
--- a/docs/rules/no-unstable-nested-components.md
+++ b/docs/rules/no-unstable-nested-components.md
@@ -125,6 +125,7 @@ function Component() {
{
"allowAsProps": true | false,
"customValidators": [] /* optional array of validators used for propTypes validation */
+ "propNamePattern": string
}
]
...
@@ -148,6 +149,16 @@ function Component() {
}
```
+You can allow other render prop naming conventions by setting the `propNamePattern` option. By default this option is `"render*"`.
+
+For example, if `propNamePattern` is set to `"*Renderer"` the following pattern is **not** considered warnings:
+
+```jsx
+
}
+/>
+```
+
## When Not To Use It
If you are not interested in preventing bugs related to re-creation of the nested components or do not care about optimization of virtual DOM.
diff --git a/lib/rules/no-unstable-nested-components.js b/lib/rules/no-unstable-nested-components.js
index e17f65e487..c330795ff0 100644
--- a/lib/rules/no-unstable-nested-components.js
+++ b/lib/rules/no-unstable-nested-components.js
@@ -5,6 +5,7 @@
'use strict';
+const minimatch = require('minimatch');
const Components = require('../util/Components');
const docsUrl = require('../util/docsUrl');
const astUtil = require('../util/ast');
@@ -32,12 +33,13 @@ function generateErrorMessageWithParentName(parentName) {
}
/**
- * Check whether given text starts with `render`. Comparison is case-sensitive.
+ * Check whether given text matches the pattern passed in.
* @param {string} text Text to validate
+ * @param {string} pattern Pattern to match against
* @returns {boolean}
*/
-function startsWithRender(text) {
- return typeof text === 'string' && text.startsWith('render');
+function propMatchesRenderPropPattern(text, pattern) {
+ return typeof text === 'string' && minimatch(text, pattern);
}
/**
@@ -165,15 +167,16 @@ function isReturnStatementOfHook(node, context) {
* ```
* @param {ASTNode} node The AST node
* @param {Context} context eslint context
+ * @param {string} propNamePattern a pattern to match render props against
* @returns {boolean} True if component is declared inside a render prop, false if not
*/
-function isComponentInRenderProp(node, context) {
+function isComponentInRenderProp(node, context, propNamePattern) {
if (
node
&& node.parent
&& node.parent.type === 'Property'
&& node.parent.key
- && startsWithRender(node.parent.key.name)
+ && propMatchesRenderPropPattern(node.parent.key.name, propNamePattern)
) {
return true;
}
@@ -202,7 +205,7 @@ function isComponentInRenderProp(node, context) {
const propName = jsxExpressionContainer.parent.name.name;
// Starts with render, e.g. } />
- if (startsWithRender(propName)) {
+ if (propMatchesRenderPropPattern(propName, propNamePattern)) {
return true;
}
@@ -222,16 +225,17 @@ function isComponentInRenderProp(node, context) {
* }] } />
* ```
* @param {ASTNode} node The AST node
+ * @param {string} propNamePattern The pattern to match render props against
* @returns {boolean} True if component is declared inside a render property, false if not
*/
-function isDirectValueOfRenderProperty(node) {
+function isDirectValueOfRenderProperty(node, propNamePattern) {
return (
node
&& node.parent
&& node.parent.type === 'Property'
&& node.parent.key
&& node.parent.key.type === 'Identifier'
- && startsWithRender(node.parent.key.name)
+ && propMatchesRenderPropPattern(node.parent.key.name, propNamePattern)
);
}
@@ -277,6 +281,9 @@ module.exports = {
allowAsProps: {
type: 'boolean',
},
+ propNamePattern: {
+ type: 'string',
+ },
},
additionalProperties: false,
}],
@@ -284,6 +291,7 @@ module.exports = {
create: Components.detect((context, components, utils) => {
const allowAsProps = context.options.some((option) => option && option.allowAsProps);
+ const propNamePattern = (context.options[0] || {}).propNamePattern || 'render*';
/**
* Check whether given node is declared inside class component's render block
@@ -418,7 +426,7 @@ module.exports = {
if (
// Support allowAsProps option
- (isDeclaredInsideProps && (allowAsProps || isComponentInRenderProp(node, context)))
+ (isDeclaredInsideProps && (allowAsProps || isComponentInRenderProp(node, context, propNamePattern)))
// Prevent reporting components created inside Array.map calls
|| isMapCall(node)
@@ -428,7 +436,7 @@ module.exports = {
|| isReturnStatementOfHook(node, context)
// Do not mark objects containing render methods
- || isDirectValueOfRenderProperty(node)
+ || isDirectValueOfRenderProperty(node, propNamePattern)
// Prevent reporting nested class components twice
|| isInsideRenderMethod(node)
diff --git a/tests/lib/rules/no-unstable-nested-components.js b/tests/lib/rules/no-unstable-nested-components.js
index 3e20ad648a..8a9c0aad47 100644
--- a/tests/lib/rules/no-unstable-nested-components.js
+++ b/tests/lib/rules/no-unstable-nested-components.js
@@ -580,6 +580,18 @@ ruleTester.run('no-unstable-nested-components', rule, {
allowAsProps: true,
}],
},
+ {
+ code: `
+ function ParentComponent() {
+ return
}
+ />
+ }
+ `,
+ options: [{
+ propNamePattern: '*Renderer',
+ }],
+ },
/* TODO These minor cases are currently falsely marked due to component detection
{
code: `