Skip to content

Commit

Permalink
[New] no-unstable-nested-components: Prevent creating unstable comp…
Browse files Browse the repository at this point in the history
…onents inside components
  • Loading branch information
AriPerkkio committed Oct 15, 2020
1 parent 2b0d70c commit 88d4172
Show file tree
Hide file tree
Showing 5 changed files with 1,501 additions and 0 deletions.
1 change: 1 addition & 0 deletions README.md
Expand Up @@ -132,6 +132,7 @@ Enable the rules that you would like to use.
* [react/no-unescaped-entities](docs/rules/no-unescaped-entities.md): Detect unescaped HTML entities, which might represent malformed tags
* [react/no-unknown-property](docs/rules/no-unknown-property.md): Prevent usage of unknown DOM property (fixable)
* [react/no-unsafe](docs/rules/no-unsafe.md): Prevent usage of unsafe lifecycle methods
* [react/no-unstable-nested-components](docs/rules/no-unstable-nested-components.md): Prevent creating unstable components inside components
* [react/no-unused-prop-types](docs/rules/no-unused-prop-types.md): Prevent definitions of unused prop types
* [react/no-unused-state](docs/rules/no-unused-state.md): Prevent definition of unused state fields
* [react/no-will-update-set-state](docs/rules/no-will-update-set-state.md): Prevent usage of setState in componentWillUpdate
Expand Down
142 changes: 142 additions & 0 deletions docs/rules/no-unstable-nested-components.md
@@ -0,0 +1,142 @@
# Prevent creating unstable components inside components (react/no-unstable-nested-components)

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.

React reconcilation 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).

## Rule Details

The following patterns are considered warnings:

```jsx
function Component() {
function UnstableNestedComponent() {
return <div />;
}

return (
<div>
<UnstableNestedComponent />
</div>
);
}
```

```jsx
function SomeComponent({ footer: Footer }) {
return (
<div>
<Footer />
</div>
);
}

function Component() {
return (
<div>
<SomeComponent footer={() => <div />} />
</div>
);
}
```

```jsx
class Component extends React.Component {
render() {
function UnstableNestedComponent() {
return <div />;
}

return (
<div>
<UnstableNestedComponent />
</div>
);
}
}
```

The following patterns are **not** considered warnings:

```jsx
function OutsideDefinedComponent(props) {
return <div />;
}

function Component() {
return (
<div>
<OutsideDefinedComponent />
</div>
);
}
```

```jsx
function Component() {
const MemoizedNestedComponent = React.useCallback(() => <div />, []);

return (
<div>
<MemoizedNestedComponent />
</div>
);
}
```

```jsx
function Component() {
return (
<SomeComponent footer={<div />} />
)
}
```

By default component creation is allowed inside component props only if prop name starts with `render`. See `allowAsProps` option for disabling this limitation completely.

```jsx
function SomeComponent(props) {
return <div>{props.renderFooter()}</div>;
}

function Component() {
return (
<div>
<SomeComponent renderFooter={() => <div />} />
</div>
);
}
```

## Rule Options

```js
...
"react/no-unstable-nested-components": [
"off" | "warn" | "error",
{ "allowAsProps": true | false }
]
...
```

You can allow component creation inside component props by setting `allowAsProps` option to true. When using this option make sure you are **calling** the props in the receiving component and not using them as elements.

The following patterns are **not** considered warnings:

```jsx
function SomeComponent(props) {
return <div>{props.footer()}</div>;
}

function Component() {
return (
<div>
<SomeComponent footer={() => <div />} />
</div>
);
}
```

## 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.
1 change: 1 addition & 0 deletions index.js
Expand Up @@ -74,6 +74,7 @@ const allRules = {
'no-unescaped-entities': require('./lib/rules/no-unescaped-entities'),
'no-unknown-property': require('./lib/rules/no-unknown-property'),
'no-unsafe': require('./lib/rules/no-unsafe'),
'no-unstable-nested-components': require('./lib/rules/no-unstable-nested-components'),
'no-unused-prop-types': require('./lib/rules/no-unused-prop-types'),
'no-unused-state': require('./lib/rules/no-unused-state'),
'no-will-update-set-state': require('./lib/rules/no-will-update-set-state'),
Expand Down

0 comments on commit 88d4172

Please sign in to comment.