Skip to content

Commit

Permalink
Upgrade to React 18 (#2087) (#2110)
Browse files Browse the repository at this point in the history
Upgrade to React 18

* replace ReactDOM.render with createRoot in index.ts
* fix new TS errors after upgrading @types
* replace ReactDOM.render in Preview.tsx
* replace ReactDOM.unmountComponentAtNode with root.unmount()
* remove ReactDOM import
* upgrade react testing library
* replace deprecated React.SFC type with React.FC

DefinitelyTyped/DefinitelyTyped#30364

* Fix TS errors in propTypes.children

propTypes.node results in TS errors. There doesn't seem to be a good alternative ( other than making the children propTypes more strict, which could result more errors)
Instead use something less restrictive (propTypes.any) for children and components.

* fix TS errors, extract props interfaces with children
* fix TS errors, add context types
* TS fix
* Fix React unmount error

Unmount asynchronously with setTimout
https://stackoverflow.com/questions/73459382/react-18-async-way-to-unmount-root

error message:
Warning: Attempted to synchronously unmount a root while React was already rendering. React cannot finish unmounting the root until the current render has completed, which may lead to a race condition.

* make wrapper unmount test async

---------

Co-authored-by: Thomas Roest <thomas.roest@moxio.com>
  • Loading branch information
ThomasRoest and Thomas Roest committed Feb 10, 2023
1 parent 2ba612a commit 310b08a
Show file tree
Hide file tree
Showing 43 changed files with 758 additions and 653 deletions.
1,147 changes: 603 additions & 544 deletions package-lock.json

Large diffs are not rendered by default.

12 changes: 6 additions & 6 deletions package.json
Expand Up @@ -106,7 +106,7 @@
"@babel/preset-react": "^7.18.6",
"@babel/preset-typescript": "^7.18.6",
"@testing-library/jest-dom": "^5.11.4",
"@testing-library/react": "^11.1.0",
"@testing-library/react": "^13.4.0",
"@types/buble": "^0.19.2",
"@types/copy-webpack-plugin": "^5.0.2",
"@types/doctrine": "0.0.3",
Expand All @@ -119,8 +119,8 @@
"@types/markdown-to-jsx": "^6.9.0",
"@types/node": "^12.12.3",
"@types/prismjs": "^1.16.0",
"@types/react": "^16.9.27",
"@types/react-dom": "^16.9.5",
"@types/react": "^18.0.26",
"@types/react-dom": "^18.0.9",
"@types/terser-webpack-plugin": "^2.2.3",
"@types/type-detect": "^4.0.1",
"@types/webpack": "^5.28.0",
Expand Down Expand Up @@ -153,9 +153,9 @@
"memfs": "~2.15.5",
"prettier": "2.1.2",
"raf": "^3.4.1",
"react": "^17.0.2",
"react-dom": "^17.0.2",
"react-test-renderer": "^17.0.2",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-test-renderer": "^18.2.0",
"strip-shebang": "^1.0.2",
"style-loader": "^3.3.1",
"typescript": "^4.7.4",
Expand Down
18 changes: 13 additions & 5 deletions src/client/index.ts
@@ -1,7 +1,7 @@
/* eslint-disable import/first */
import './polyfills';
import './styles';
import ReactDOM from 'react-dom';
import { createRoot, Root } from 'react-dom/client';
import renderStyleguide from './utils/renderStyleguide';
import { getParameterByName, hasInHash, getHash } from './utils/handleHash';

Expand Down Expand Up @@ -33,14 +33,22 @@ const scrollToOrigin = () => {
}
};

let reactRoot: Root | null = null;

const render = () => {
// eslint-disable-next-line @typescript-eslint/no-var-requires
const styleguide = require('!!../loaders/styleguide-loader!./index.js');

ReactDOM.render(
renderStyleguide(styleguide, codeRevision),
document.getElementById(styleguide.config.mountPointId)
);
if (!reactRoot) {
const rootNode = document.getElementById(styleguide.config.mountPointId);
if (rootNode) {
reactRoot = createRoot(rootNode);
}
}

if (reactRoot) {
reactRoot.render(renderStyleguide(styleguide, codeRevision));
}
};

window.addEventListener('hashchange', render);
Expand Down
2 changes: 1 addition & 1 deletion src/client/rsg-components/Code/CodeRenderer.tsx
Expand Up @@ -22,7 +22,7 @@ export const CodeRenderer: React.FunctionComponent<CodeProps> = ({ classes, chil
};
CodeRenderer.propTypes = {
classes: PropTypes.objectOf(PropTypes.string.isRequired).isRequired,
children: PropTypes.node.isRequired,
children: PropTypes.any.isRequired,
};

export default Styled<CodeProps>(styles)(CodeRenderer);
2 changes: 1 addition & 1 deletion src/client/rsg-components/Examples/ExamplesRenderer.tsx
Expand Up @@ -27,7 +27,7 @@ export const ExamplesRenderer: React.FunctionComponent<ExamplesRendererProps> =
ExamplesRenderer.propTypes = {
classes: PropTypes.objectOf(PropTypes.string.isRequired).isRequired,
name: PropTypes.string.isRequired,
children: PropTypes.node,
children: PropTypes.any,
};

export default Styled<ExamplesRendererProps>(styles)(ExamplesRenderer);
4 changes: 2 additions & 2 deletions src/client/rsg-components/Heading/HeadingRenderer.tsx
Expand Up @@ -44,7 +44,7 @@ const HeadingRenderer: React.FunctionComponent<HeadingProps> = ({
children,
...props
}) => {
const Tag = `h${level}` as ('h1' | 'h2' | 'h3' | 'h4' | 'h5' | 'h6');
const Tag = `h${level}` as 'h1' | 'h2' | 'h3' | 'h4' | 'h5' | 'h6';
const headingClasses = cx(classes.heading, classes[`heading${level}`]);

return (
Expand All @@ -57,7 +57,7 @@ const HeadingRenderer: React.FunctionComponent<HeadingProps> = ({
HeadingRenderer.propTypes = {
classes: PropTypes.objectOf(PropTypes.string.isRequired).isRequired,
level: PropTypes.oneOf([1, 2, 3, 4, 5, 6]).isRequired,
children: PropTypes.node,
children: PropTypes.any,
};

export default Styled<HeadingProps>(styles)(HeadingRenderer);
2 changes: 1 addition & 1 deletion src/client/rsg-components/Link/LinkRenderer.tsx
Expand Up @@ -41,7 +41,7 @@ export const LinkRenderer: React.FunctionComponent<LinkProps> = ({

LinkRenderer.propTypes = {
classes: PropTypes.objectOf(PropTypes.string.isRequired).isRequired,
children: PropTypes.node,
children: PropTypes.any,
className: PropTypes.string,
href: PropTypes.string,
};
Expand Down
6 changes: 5 additions & 1 deletion src/client/rsg-components/Logo/LogoRenderer.tsx
Expand Up @@ -12,7 +12,11 @@ const styles = ({ color, fontFamily, fontSize }: Rsg.Theme) => ({
},
});

export const LogoRenderer: React.FunctionComponent<JssInjectedProps> = ({ classes, children }) => {
interface Props extends JssInjectedProps {
children?: React.ReactNode;
}

export const LogoRenderer = ({ classes, children }: Props) => {
return <h1 className={classes.logo}>{children}</h1>;
};

Expand Down
Expand Up @@ -32,7 +32,7 @@ export const BlockquoteRenderer: React.FunctionComponent<BlockquoteProps> = ({
BlockquoteRenderer.propTypes = {
classes: PropTypes.objectOf(PropTypes.string.isRequired).isRequired,
className: PropTypes.string,
children: PropTypes.node.isRequired,
children: PropTypes.any.isRequired,
};

export default Styled<BlockquoteProps>(styles)(BlockquoteRenderer);
Expand Up @@ -22,7 +22,7 @@ export const DetailsRenderer: React.FunctionComponent<DetailsProps> = ({ classes

DetailsRenderer.propTypes = {
classes: PropTypes.objectOf(PropTypes.string.isRequired).isRequired,
children: PropTypes.node.isRequired,
children: PropTypes.any.isRequired,
};

export default Styled<DetailsProps>(styles)(DetailsRenderer);
Expand Up @@ -32,7 +32,7 @@ export const DetailsSummaryRenderer: React.FunctionComponent<DetailsSummaryProps

DetailsSummaryRenderer.propTypes = {
classes: PropTypes.objectOf(PropTypes.string.isRequired).isRequired,
children: PropTypes.node.isRequired,
children: PropTypes.any.isRequired,
};

export default Styled<DetailsSummaryProps>(styles)(DetailsSummaryRenderer);
4 changes: 2 additions & 2 deletions src/client/rsg-components/Markdown/List/ListRenderer.tsx
Expand Up @@ -39,7 +39,7 @@ export const ListRenderer: React.FunctionComponent<ListProps> = ({

return (
<Tag className={classNames}>
{Children.map(children, li =>
{Children.map(children, (li) =>
React.isValidElement(li) ? cloneElement(li, { className: classes.li }) : li
)}
</Tag>
Expand All @@ -48,7 +48,7 @@ export const ListRenderer: React.FunctionComponent<ListProps> = ({
ListRenderer.propTypes = {
classes: PropTypes.objectOf(PropTypes.string.isRequired).isRequired,
ordered: PropTypes.bool,
children: PropTypes.node.isRequired,
children: PropTypes.any.isRequired,
};
ListRenderer.defaultProps = {
ordered: false,
Expand Down
52 changes: 26 additions & 26 deletions src/client/rsg-components/Markdown/Markdown.tsx
@@ -1,4 +1,4 @@
import React, { isValidElement } from 'react';
import React, { isValidElement, PropsWithChildren } from 'react';
import PropTypes from 'prop-types';
import { compiler } from 'markdown-to-jsx';
import stripHtmlComments from 'strip-html-comments';
Expand Down Expand Up @@ -28,112 +28,112 @@ Pre.propTypes = {

export const baseOverrides = {
a: {
component: Link as React.SFC,
component: Link as React.FC,
},
h1: {
component: MarkdownHeading as React.SFC,
component: MarkdownHeading as React.FC,
props: {
level: 1,
},
},
h2: {
component: MarkdownHeading as React.SFC,
component: MarkdownHeading as React.FC,
props: {
level: 2,
},
},
h3: {
component: MarkdownHeading as React.SFC,
component: MarkdownHeading as React.FC,
props: {
level: 3,
},
},
h4: {
component: MarkdownHeading as React.SFC,
component: MarkdownHeading as React.FC,
props: {
level: 4,
},
},
h5: {
component: MarkdownHeading as React.SFC,
component: MarkdownHeading as React.FC,
props: {
level: 5,
},
},
h6: {
component: MarkdownHeading as React.SFC,
component: MarkdownHeading as React.FC,
props: {
level: 6,
},
},
p: {
component: Para as React.SFC,
component: Para as React.FC,
props: {
semantic: 'p',
},
},
em: {
component: Text as React.SFC,
component: Text as React.FC,
props: {
semantic: 'em',
},
},
strong: {
component: Text as React.SFC,
component: Text as React.FC,
props: {
semantic: 'strong',
},
},
ul: {
component: List as React.SFC,
component: List as React.FC,
},
ol: {
component: List as React.SFC,
component: List as React.FC,
props: {
ordered: true,
},
},
blockquote: {
component: Blockquote as React.SFC,
component: Blockquote as React.FC,
},
code: {
component: Code as React.SFC,
component: Code as React.FC,
},
pre: {
component: Pre as React.SFC,
component: Pre as React.FC<PropsWithChildren>,
},
input: {
component: Checkbox as React.SFC,
component: Checkbox as React.FC,
},
hr: {
component: Hr as React.SFC,
component: Hr as React.FC,
},
table: {
component: Table as React.SFC,
component: Table as React.FC,
},
thead: {
component: TableHead as React.SFC,
component: TableHead as React.FC,
},
th: {
component: TableCell as React.SFC,
component: TableCell as React.FC,
props: {
header: true,
},
},
tbody: {
component: TableBody as React.SFC,
component: TableBody as React.FC,
},
tr: {
component: TableRow as React.SFC,
component: TableRow as React.FC,
},
td: {
component: TableCell as React.SFC,
component: TableCell as React.FC,
},
details: {
component: Details as React.SFC,
component: Details as React.FC,
},
summary: {
component: DetailsSummary as React.SFC,
component: DetailsSummary as React.FC,
},
};

Expand Down
Expand Up @@ -34,7 +34,7 @@ const MarkdownHeadingRenderer: React.FunctionComponent<MarkdownHeadingProps> = (
MarkdownHeadingRenderer.propTypes = {
classes: PropTypes.objectOf(PropTypes.string.isRequired).isRequired,
level: PropTypes.oneOf([1, 2, 3, 4, 5, 6]).isRequired,
children: PropTypes.node,
children: PropTypes.any,
id: PropTypes.string,
};

Expand Down
2 changes: 1 addition & 1 deletion src/client/rsg-components/Markdown/Pre/PreRenderer.tsx
Expand Up @@ -50,7 +50,7 @@ export const PreRenderer: React.FunctionComponent<PrePropsWithClasses> = ({
PreRenderer.propTypes = {
classes: PropTypes.objectOf(PropTypes.string.isRequired).isRequired,
className: PropTypes.string,
children: PropTypes.node.isRequired,
children: PropTypes.any.isRequired,
};

export default Styled<PrePropsWithClasses>(styles)(PreRenderer);
@@ -1,7 +1,11 @@
import React from 'react';
import PropTypes from 'prop-types';

export const TableBodyRenderer: React.FunctionComponent = ({ children }) => {
interface Props {
children?: React.ReactNode;
}

export const TableBodyRenderer = ({ children }: Props) => {
return <tbody>{children}</tbody>;
};
TableBodyRenderer.propTypes = {
Expand Down
Expand Up @@ -37,7 +37,7 @@ export const TableCellRenderer: React.FunctionComponent<TableCellProps> = ({
TableCellRenderer.propTypes = {
classes: PropTypes.objectOf(PropTypes.string.isRequired).isRequired,
header: PropTypes.bool,
children: PropTypes.node.isRequired,
children: PropTypes.any.isRequired,
};
TableCellRenderer.defaultProps = {
header: false,
Expand Down
Expand Up @@ -22,7 +22,7 @@ export const TableHeadRenderer: React.FunctionComponent<TableHeadProps> = ({

TableHeadRenderer.propTypes = {
classes: PropTypes.objectOf(PropTypes.string.isRequired).isRequired,
children: PropTypes.node.isRequired,
children: PropTypes.any.isRequired,
};

export default Styled<TableHeadProps>(styles)(TableHeadRenderer);
2 changes: 1 addition & 1 deletion src/client/rsg-components/Markdown/Table/TableRenderer.tsx
Expand Up @@ -21,7 +21,7 @@ export const TableRenderer: React.FunctionComponent<TableProps> = ({ classes, ch

TableRenderer.propTypes = {
classes: PropTypes.objectOf(PropTypes.string.isRequired).isRequired,
children: PropTypes.node.isRequired,
children: PropTypes.any.isRequired,
};

export default Styled<TableProps>(styles)(TableRenderer);
@@ -1,7 +1,11 @@
import React from 'react';
import PropTypes from 'prop-types';

export const TableRowRenderer: React.FunctionComponent = ({ children }) => {
interface Props {
children?: React.ReactNode;
}

export const TableRowRenderer = ({ children }: Props) => {
return <tr>{children}</tr>;
};
TableRowRenderer.propTypes = {
Expand Down
2 changes: 1 addition & 1 deletion src/client/rsg-components/Message/MessageRenderer.tsx
Expand Up @@ -32,7 +32,7 @@ export const MessageRenderer: React.FunctionComponent<MessageProps> = ({ classes

MessageRenderer.propTypes = {
classes: PropTypes.objectOf(PropTypes.string.isRequired).isRequired,
children: PropTypes.node.isRequired,
children: PropTypes.any.isRequired,
};

export default Styled<MessageProps>(styles)(MessageRenderer);

0 comments on commit 310b08a

Please sign in to comment.