Skip to content

Commit 3badf6f

Browse files
committed
Add support for newer versions of react-dom
In v19, you need to import from react-dom/client and use the createRoot API.
1 parent 8f8345b commit 3badf6f

File tree

5 files changed

+67
-38
lines changed

5 files changed

+67
-38
lines changed

.github/workflows/test.yml

+7-1
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,8 @@ jobs:
1212
strategy:
1313
matrix:
1414
node: ['18.16.0', 20, 22]
15-
name: Node ${{ matrix.node }}
15+
react: ['16', '17', '18', '19']
16+
name: Node ${{ matrix.node }} / React ${{ matrix.react }}
1617
steps:
1718
- uses: actions/checkout@v4
1819

@@ -24,6 +25,11 @@ jobs:
2425

2526
- run: yarn install --frozen-lockfile --ignore-engines
2627

28+
- name: Install react and react-dom at version ${{ matrix.react }}
29+
run: |
30+
yarn remove react react-dom
31+
yarn add react@${{ matrix.react }} react-dom@${{ matrix.react }}
32+
2733
- run: yarn test --colors
2834

2935
lint:

package.json

+2-2
Original file line numberDiff line numberDiff line change
@@ -95,8 +95,8 @@
9595
"jest": "^29.7.0",
9696
"multiparty": "^4.2.2",
9797
"prettier": "^3.5.1",
98-
"react": "^16.3.2",
99-
"react-dom": "^16.3.2",
98+
"react": "^18.3.1",
99+
"react-dom": "^18.3.1",
100100
"rimraf": "^6.0.1",
101101
"webpack": "^5.2.0"
102102
},

src/createDynamicEntryPoint.js

+39-13
Original file line numberDiff line numberDiff line change
@@ -51,25 +51,51 @@ export default async function createDynamicEntryPoint({
5151
'window.snaps = {};',
5252
];
5353
if (type === 'react') {
54-
const pathToReactDom = require.resolve('react-dom');
5554
const pathToRW = renderWrapperModule || require.resolve('./renderWrapperReact');
5655
strings.push(
5756
`let renderWrapper = require('${pathToRW}');`,
5857
'renderWrapper = renderWrapper.default || renderWrapper;',
5958
);
60-
strings.push(
61-
`
62-
const ReactDOM = require('${pathToReactDom}');
63-
window.happoRender = (reactComponent, { rootElement, component, variant }) =>
64-
ReactDOM.render(renderWrapper(reactComponent, { component, variant }), rootElement);
6559

66-
window.happoCleanup = () => {
67-
for (const element of document.body.children) {
68-
ReactDOM.unmountComponentAtNode(element);
69-
}
70-
};
71-
`.trim(),
72-
);
60+
// eslint-disable-next-line import/no-extraneous-dependencies
61+
const reactDomVersion = require('react-dom/package.json').version;
62+
const reactDomMajorVersion = parseInt(reactDomVersion.split('.', 1)[0], 10);
63+
if (reactDomMajorVersion >= 18) {
64+
const pathToReactDom = require.resolve('react-dom/client');
65+
strings.push(
66+
`
67+
const ReactDOM = require('${pathToReactDom}');
68+
let root;
69+
window.happoRender = (reactComponent, { rootElement, component, variant }) => {
70+
root = ReactDOM.createRoot(rootElement);
71+
root.render(renderWrapper(reactComponent, { component, variant }));
72+
};
73+
74+
window.happoCleanup = () => {
75+
if (root) {
76+
root.unmount();
77+
}
78+
};
79+
`.trim(),
80+
);
81+
} else {
82+
// Older versions of React DOM
83+
const pathToReactDom = require.resolve('react-dom');
84+
strings.push(
85+
`
86+
const ReactDOM = require('${pathToReactDom}');
87+
window.happoRender = (reactComponent, { rootElement, component, variant }) => {
88+
ReactDOM.render(renderWrapper(reactComponent, { component, variant }), rootElement);
89+
};
90+
91+
window.happoCleanup = () => {
92+
for (const element of document.body.children) {
93+
ReactDOM.unmountComponentAtNode(element);
94+
}
95+
};
96+
`.trim(),
97+
);
98+
}
7399
} else {
74100
const pathToRW = renderWrapperModule || require.resolve('./renderWrapper');
75101
strings.push(

test/integrations/examples/Foo-react-happo.js

+5-3
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import React from 'react';
2-
import ReactDOM from 'react-dom';
2+
import { createPortal } from 'react-dom';
33

44
import Button from './Button.ffs';
55
import ThemeContext from '../theme';
@@ -22,7 +22,7 @@ export const anotherVariant = () => {
2222
const PortalComponent = ({ children }) => {
2323
const element = document.createElement('div');
2424
document.body.appendChild(element);
25-
return ReactDOM.createPortal(children, element);
25+
return createPortal(children, document.body);
2626
};
2727

2828
export const portalExample = () => (
@@ -44,7 +44,9 @@ export const innerPortal = () => (
4444
class AsyncComponent extends React.Component {
4545
constructor(props) {
4646
super(props);
47-
this.state = {};
47+
this.state = {
48+
label: 'Not ready',
49+
};
4850
this.setLabel = this.setLabel.bind(this);
4951
}
5052

yarn.lock

+14-19
Original file line numberDiff line numberDiff line change
@@ -4939,7 +4939,7 @@ prompts@^2.0.1:
49394939
kleur "^3.0.3"
49404940
sisteransi "^1.0.5"
49414941

4942-
prop-types@^15.6.2, prop-types@^15.8.1:
4942+
prop-types@^15.8.1:
49434943
version "15.8.1"
49444944
resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.8.1.tgz#67d87bf1a694f48435cf332c24af10214a3140b5"
49454945
integrity sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==
@@ -5016,15 +5016,13 @@ randombytes@^2.1.0:
50165016
dependencies:
50175017
safe-buffer "^5.1.0"
50185018

5019-
react-dom@^16.3.2:
5020-
version "16.14.0"
5021-
resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-16.14.0.tgz#7ad838ec29a777fb3c75c3a190f661cf92ab8b89"
5022-
integrity sha512-1gCeQXDLoIqMgqD3IO2Ah9bnf0w9kzhwN5q4FGnHZ67hBm9yePzB5JJAIQCc8x3pFnNlwFq4RidZggNAAkzWWw==
5019+
react-dom@^18.3.1:
5020+
version "18.3.1"
5021+
resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-18.3.1.tgz#c2265d79511b57d479b3dd3fdfa51536494c5cb4"
5022+
integrity sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw==
50235023
dependencies:
50245024
loose-envify "^1.1.0"
5025-
object-assign "^4.1.1"
5026-
prop-types "^15.6.2"
5027-
scheduler "^0.19.1"
5025+
scheduler "^0.23.2"
50285026

50295027
react-is@^16.13.1:
50305028
version "16.13.1"
@@ -5036,14 +5034,12 @@ react-is@^18.0.0:
50365034
resolved "https://registry.yarnpkg.com/react-is/-/react-is-18.3.1.tgz#e83557dc12eae63a99e003a46388b1dcbb44db7e"
50375035
integrity sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==
50385036

5039-
react@^16.3.2:
5040-
version "16.14.0"
5041-
resolved "https://registry.yarnpkg.com/react/-/react-16.14.0.tgz#94d776ddd0aaa37da3eda8fc5b6b18a4c9a3114d"
5042-
integrity sha512-0X2CImDkJGApiAlcf0ODKIneSwBPhqJawOa5wCtKbu7ZECrmS26NvtSILynQ66cgkT/RJ4LidJOc3bUESwmU8g==
5037+
react@^18.3.1:
5038+
version "18.3.1"
5039+
resolved "https://registry.yarnpkg.com/react/-/react-18.3.1.tgz#49ab892009c53933625bd16b2533fc754cab2891"
5040+
integrity sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==
50435041
dependencies:
50445042
loose-envify "^1.1.0"
5045-
object-assign "^4.1.1"
5046-
prop-types "^15.6.2"
50475043

50485044
readable-stream@^2.0.5, readable-stream@^2.2.2:
50495045
version "2.3.7"
@@ -5294,13 +5290,12 @@ saxes@^5.0.1:
52945290
dependencies:
52955291
xmlchars "^2.2.0"
52965292

5297-
scheduler@^0.19.1:
5298-
version "0.19.1"
5299-
resolved "https://registry.yarnpkg.com/scheduler/-/scheduler-0.19.1.tgz#4f3e2ed2c1a7d65681f4c854fa8c5a1ccb40f196"
5300-
integrity sha512-n/zwRWRYSUj0/3g/otKDRPMh6qv2SYMWNq85IEa8iZyAv8od9zDYpGSnpBEjNgcMNq6Scbu5KfIPxNF72R/2EA==
5293+
scheduler@^0.23.2:
5294+
version "0.23.2"
5295+
resolved "https://registry.yarnpkg.com/scheduler/-/scheduler-0.23.2.tgz#414ba64a3b282892e944cf2108ecc078d115cdc3"
5296+
integrity sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ==
53015297
dependencies:
53025298
loose-envify "^1.1.0"
5303-
object-assign "^4.1.1"
53045299

53055300
schema-utils@^3.0.0, schema-utils@^3.1.1, schema-utils@^3.2.0:
53065301
version "3.3.0"

0 commit comments

Comments
 (0)