Skip to content

Commit

Permalink
[New] no-deprecated: add React 18 deprecations
Browse files Browse the repository at this point in the history
  • Loading branch information
sergei-startsev authored and ljharb committed Mar 9, 2023
1 parent 45184ef commit 838ac07
Show file tree
Hide file tree
Showing 4 changed files with 175 additions and 3 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,14 @@ This change log adheres to standards from [Keep a CHANGELOG](https://keepachange
### Added
* [`display-name`]: add `checkContextObjects` option ([#3529][] @JulesBlm)
* [`jsx-first-prop-new-line`]: add `multiprop` option ([#3533][] @haydncomley)
* [`no-deprecated`]: add React 18 deprecations ([#3548][] @sergei-startsev)

### Fixed
* [`no-array-index-key`]: consider flatMap ([#3530][] @k-yle)
* [`jsx-curly-brace-presence`]: handle single and only expression template literals ([#3538][] @taozhou-glean)
* [`no-unknown-property`]: allow `onLoad` on `source` (@ljharb)

[#3548]: https://github.com/jsx-eslint/eslint-plugin-react/pull/3548
[#3538]: https://github.com/jsx-eslint/eslint-plugin-react/pull/3538
[#3533]: https://github.com/jsx-eslint/eslint-plugin-react/pull/3533
[#3530]: https://github.com/jsx-eslint/eslint-plugin-react/pull/3530
Expand Down
25 changes: 24 additions & 1 deletion docs/rules/no-deprecated.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,19 +36,42 @@ import React, { PropTypes } from 'react';
componentWillMount() { }
componentWillReceiveProps() { }
componentWillUpdate() { }

// React 18 deprecations
import { render } from 'react-dom';
ReactDOM.render(<div></div>, container);

import { hydrate } from 'react-dom';
ReactDOM.hydrate(<div></div>, container);

import {unmountComponentAtNode} from 'react-dom';
ReactDOM.unmountComponentAtNode(container);

import { renderToNodeStream } from 'react-dom/server';
ReactDOMServer.renderToNodeStream(element);
```

Examples of **correct** code for this rule:

```jsx
// when React < 18
ReactDOM.render(<MyComponent />, root);

// When [1, {"react": "0.13.0"}]
// when React is < 0.14
ReactDOM.findDOMNode(this.refs.foo);

import { PropTypes } from 'prop-types';

UNSAFE_componentWillMount() { }
UNSAFE_componentWillReceiveProps() { }
UNSAFE_componentWillUpdate() { }

ReactDOM.createPortal(child, container);

import { createRoot } from 'react-dom/client';
const root = createRoot(container);
root.unmount();

import { hydrateRoot } from 'react-dom/client';
const root = hydrateRoot(container, <App/>);
```
25 changes: 25 additions & 0 deletions lib/rules/no-deprecated.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ const report = require('../util/report');
const MODULES = {
react: ['React'],
'react-addons-perf': ['ReactPerf', 'Perf'],
'react-dom': ['ReactDOM'],
'react-dom/server': ['ReactDOMServer'],
};

// ------------------------------------------------------------------------------
Expand Down Expand Up @@ -82,6 +84,29 @@ function getDeprecated(pragma) {
'https://reactjs.org/docs/react-component.html#unsafe_componentwillupdate. '
+ 'Use https://github.com/reactjs/react-codemod#rename-unsafe-lifecycles to automatically update your components.',
];
// 18.0.0
// https://reactjs.org/blog/2022/03/08/react-18-upgrade-guide.html#deprecations
deprecated['ReactDOM.render'] = [
'18.0.0',
'createRoot',
'https://reactjs.org/link/switch-to-createroot',
];
deprecated['ReactDOM.hydrate'] = [
'18.0.0',
'hydrateRoot',
'https://reactjs.org/link/switch-to-createroot',
];
deprecated['ReactDOM.unmountComponentAtNode'] = [
'18.0.0',
'root.unmount',
'https://reactjs.org/link/switch-to-createroot',
];
deprecated['ReactDOMServer.renderToNodeStream'] = [
'18.0.0',
'renderToPipeableStream',
'https://reactjs.org/docs/react-dom-server.html#rendertonodestream',
];

return deprecated;
}

Expand Down
126 changes: 124 additions & 2 deletions tests/lib/rules/no-deprecated.js
Original file line number Diff line number Diff line change
Expand Up @@ -48,9 +48,9 @@ ruleTester.run('no-deprecated', rule, {
// Not deprecated
'var element = React.createElement(\'p\', {}, null);',
'var clone = React.cloneElement(element);',
'ReactDOM.render(element, container);',
'ReactDOM.unmountComponentAtNode(container);',
'ReactDOM.cloneElement(child, container);',
'ReactDOM.findDOMNode(instance);',
'ReactDOM.createPortal(child, container);',
'ReactDOMServer.renderToString(element);',
'ReactDOMServer.renderToStaticMarkup(element);',
{
Expand Down Expand Up @@ -119,6 +119,40 @@ ruleTester.run('no-deprecated', rule, {
let { default: defaultReactExport, ...allReactExports } = React;
`,
},
// React < 18
{
code: `
import { render, hydrate } from 'react-dom';
import { renderToNodeStream } from 'react-dom/server';
ReactDOM.render(element, container);
ReactDOM.unmountComponentAtNode(container);
ReactDOMServer.renderToNodeStream(element);
`,
settings: { react: { version: '17.999.999' } },
},
// React 18 API
{
code: `
import ReactDOM, { createRoot } from 'react-dom/client';
ReactDOM.createRoot(container);
const root = createRoot(container);
root.unmount();
`,
},
{
code: `
import ReactDOM, { hydrateRoot } from 'react-dom/client';
ReactDOM.hydrateRoot(container, <App/>);
hydrateRoot(container, <App/>);
`,
},
{
code: `
import ReactDOMServer, { renderToPipeableStream } from 'react-dom/server';
ReactDOMServer.renderToPipeableStream(<App />, {});
renderToPipeableStream(<App />, {});
`,
},
]),

invalid: parsers.all([
Expand Down Expand Up @@ -454,5 +488,93 @@ ruleTester.run('no-deprecated', rule, {
),
],
},
{
code: `
import { render } from 'react-dom';
ReactDOM.render(<div></div>, container);
`,
errors: [
errorMessage(
'ReactDOM.render',
'18.0.0',
'createRoot',
'https://reactjs.org/link/switch-to-createroot',
{ type: 'ImportDeclaration', line: 2, column: 9 }
),
errorMessage(
'ReactDOM.render',
'18.0.0',
'createRoot',
'https://reactjs.org/link/switch-to-createroot',
{ type: 'MemberExpression', line: 3, column: 9 }
),
],
},
{
code: `
import { hydrate } from 'react-dom';
ReactDOM.hydrate(<div></div>, container);
`,
errors: [
errorMessage(
'ReactDOM.hydrate',
'18.0.0',
'hydrateRoot',
'https://reactjs.org/link/switch-to-createroot',
{ type: 'ImportDeclaration', line: 2, column: 9 }
),
errorMessage(
'ReactDOM.hydrate',
'18.0.0',
'hydrateRoot',
'https://reactjs.org/link/switch-to-createroot',
{ type: 'MemberExpression', line: 3, column: 9 }
),
],
},
{
code: `
import { unmountComponentAtNode } from 'react-dom';
ReactDOM.unmountComponentAtNode(container);
`,
errors: [
errorMessage(
'ReactDOM.unmountComponentAtNode',
'18.0.0',
'root.unmount',
'https://reactjs.org/link/switch-to-createroot',
{ type: 'ImportDeclaration', line: 2, column: 9 }
),
errorMessage(
'ReactDOM.unmountComponentAtNode',
'18.0.0',
'root.unmount',
'https://reactjs.org/link/switch-to-createroot',
{ type: 'MemberExpression', line: 3, column: 9 }
),
],
},
{
code: `
import { renderToNodeStream } from 'react-dom/server';
ReactDOMServer.renderToNodeStream(element);
`,
errors: [
errorMessage(
'ReactDOMServer.renderToNodeStream',
'18.0.0',
'renderToPipeableStream',
'https://reactjs.org/docs/react-dom-server.html#rendertonodestream',
{ type: 'ImportDeclaration', line: 2, column: 9 }
),
errorMessage(
'ReactDOMServer.renderToNodeStream',
'18.0.0',
'renderToPipeableStream',
'https://reactjs.org/docs/react-dom-server.html#rendertonodestream',
{ type: 'MemberExpression', line: 3, column: 9 }
),
],
},
]),
});

0 comments on commit 838ac07

Please sign in to comment.