Skip to content

Commit 78ad0f0

Browse files
eps1lonljharb
authored andcommitted
[Docs] no-unstable-nested-components: Warn about memoized, nested components
1 parent d9a51af commit 78ad0f0

File tree

3 files changed

+22
-10
lines changed

3 files changed

+22
-10
lines changed

CHANGELOG.md

+2
Original file line numberDiff line numberDiff line change
@@ -14,10 +14,12 @@ This change log adheres to standards from [Keep a CHANGELOG](https://keepachange
1414
### Changed
1515
* [Docs] [`no-unknown-property`]: fix typo in link ([#3445][] @denkristoffer)
1616
* [Perf] component detection: improve performance by optimizing getId ([#3451][] @golopot)
17+
* [Docs] [`no-unstable-nested-components`]: Warn about memoized, nested components ([#3444][] @eps1lon)
1718

1819
[#3451]: https://github.com/jsx-eslint/eslint-plugin-react/pull/3451
1920
[#3448]: https://github.com/jsx-eslint/eslint-plugin-react/pull/3448
2021
[#3445]: https://github.com/jsx-eslint/eslint-plugin-react/pull/3445
22+
[#3444]: https://github.com/jsx-eslint/eslint-plugin-react/pull/3444
2123
[#3436]: https://github.com/jsx-eslint/eslint-plugin-react/issues/3436
2224
[#3337]: https://github.com/jsx-eslint/eslint-plugin-react/issues/3337
2325
[#1591]: https://github.com/jsx-eslint/eslint-plugin-react/pull/1591

docs/rules/no-unstable-nested-components.md

+16-10
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,9 @@
22

33
💼 This rule is enabled in the following [configs](https://github.com/jsx-eslint/eslint-plugin-react#shareable-configurations): `all`.
44

5-
Creating components inside components without memoization leads to unstable components. The nested component and all its children are recreated during each re-render. Given stateful children of the nested component will lose their state on each re-render.
5+
Creating components inside components (nested components) will cause React to throw away the state of those nested components on each re-render of their parent.
66

7-
React reconciliation performs element type comparison with [reference equality](https://github.com/facebook/react/blob/v16.13.1/packages/react-reconciler/src/ReactChildFiber.js#L407). The reference to the same element changes on each re-render when defining components inside the render block. This leads to complete recreation of the current node and all its children. As a result the virtual DOM has to do extra unnecessary work and [possible bugs are introduced](https://codepen.io/ariperkkio/pen/vYLodLB).
7+
React reconciliation performs element type comparison with [reference equality](https://reactjs.org/docs/reconciliation.html#elements-of-different-types). The reference to the same element changes on each re-render when defining components inside the render block. This leads to complete recreation of the current node and all its children. As a result the virtual DOM has to do extra unnecessary work and [possible bugs are introduced](https://codepen.io/ariperkkio/pen/vYLodLB).
88

99
## Rule Details
1010

@@ -76,6 +76,20 @@ function Component() {
7676

7777
```jsx
7878
function Component() {
79+
return <SomeComponent footer={<div />} />;
80+
}
81+
```
82+
83+
⚠️ WARNING ⚠️:
84+
85+
Creating nested but memoized components is currently not detected by this rule but should also be avoided.
86+
If the `useCallback` or `useMemo` hook has no dependency, you can safely move the component definition out of the render function.
87+
If the hook does have dependencies, you should refactor the code so that you're able to move the component definition out of the render function.
88+
If you want React to throw away the state of the nested component, use a [`key`](https://reactjs.org/docs/lists-and-keys.html#keys) instead.
89+
90+
```jsx
91+
function Component() {
92+
// No ESLint warning but `MemoizedNestedComponent` should be moved outside of `Component`.
7993
const MemoizedNestedComponent = React.useCallback(() => <div />, []);
8094

8195
return (
@@ -86,14 +100,6 @@ function Component() {
86100
}
87101
```
88102

89-
```jsx
90-
function Component() {
91-
return (
92-
<SomeComponent footer={<div />} />
93-
)
94-
}
95-
```
96-
97103
By default component creation is allowed inside component props only if prop name starts with `render`. See `allowAsProps` option for disabling this limitation completely.
98104

99105
```jsx

tests/lib/rules/no-unstable-nested-components.js

+4
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,7 @@ ruleTester.run('no-unstable-nested-components', rule, {
7878
`,
7979
},
8080
{
81+
// false-negative.
8182
code: `
8283
function ParentComponent() {
8384
const MemoizedNestedComponent = React.useCallback(() => <div />, []);
@@ -91,6 +92,7 @@ ruleTester.run('no-unstable-nested-components', rule, {
9192
`,
9293
},
9394
{
95+
// false-negative.
9496
code: `
9597
function ParentComponent() {
9698
const MemoizedNestedComponent = React.useCallback(
@@ -107,6 +109,7 @@ ruleTester.run('no-unstable-nested-components', rule, {
107109
`,
108110
},
109111
{
112+
// false-negative.
110113
code: `
111114
function ParentComponent() {
112115
const MemoizedNestedFunctionComponent = React.useCallback(
@@ -125,6 +128,7 @@ ruleTester.run('no-unstable-nested-components', rule, {
125128
`,
126129
},
127130
{
131+
// false-negative.
128132
code: `
129133
function ParentComponent() {
130134
const MemoizedNestedFunctionComponent = React.useCallback(

0 commit comments

Comments
 (0)