diff --git a/packages/react/package.json b/packages/react/package.json
index 1ab4e1c1..c49a1525 100644
--- a/packages/react/package.json
+++ b/packages/react/package.json
@@ -31,16 +31,16 @@
},
"sideEffects": false,
"devDependencies": {
- "@types/react-dom": "^17.0.0",
- "react": "^17.0.0",
- "react-dom": "^17.0.0"
+ "@types/react-dom": "^18.0.5",
+ "react": "^18.1.0",
+ "react-dom": "^18.1.0"
},
- "dependencies": {
+ "dependencies": {
"@remote-ui/async-subscription": "^2.1.13",
"@remote-ui/core": "^2.1.16",
"@remote-ui/rpc": "^1.3.4",
- "@types/react": ">=17.0.0 <18.0.0",
- "@types/react-reconciler": "^0.26.0",
- "react-reconciler": ">=0.26.0 <0.27.0"
+ "@types/react": "^18.0.9",
+ "@types/react-reconciler": "^0.26.7",
+ "react-reconciler": "^0.28.0"
}
}
diff --git a/packages/react/src/reconciler.ts b/packages/react/src/reconciler.ts
index 290377a4..bd6e45b6 100644
--- a/packages/react/src/reconciler.ts
+++ b/packages/react/src/reconciler.ts
@@ -27,6 +27,11 @@ export type Reconciler = ReactReconciler<
PublicInstance
>;
+const scheduleMicrotask = (callback: () => void) =>
+ typeof queueMicrotask === 'function'
+ ? queueMicrotask
+ : Promise.resolve(null).then(callback).catch(handleErrorInNextTick);
+
export const reconciler = reactReconciler<
Type,
Props,
@@ -48,9 +53,18 @@ export const reconciler = reactReconciler<
scheduleTimeout: setTimeout,
cancelTimeout: clearTimeout,
noTimeout: false,
- // @see https://github.com/facebook/react/blob/master/packages/react-dom/src/client/ReactDOMHostConfig.js#L408
- queueMicrotask: (callback) =>
- Promise.resolve(null).then(callback).catch(handleErrorInNextTick),
+
+ // Microtask scheduling
+ // @see https://github.com/facebook/react/blob/2c8a1452b82b9ec5ebfa3f370b31fda19610ae92/packages/react-dom/src/client/ReactDOMHostConfig.js#L391-L401
+ // eslint-disable-next-line @typescript-eslint/ban-ts-comment
+ // @ts-ignore - types in `@types/react-reconciler` are outdated
+ supportsMicrotasks: true,
+ // eslint-disable-next-line @typescript-eslint/ban-ts-comment
+ // @ts-ignore - types in `@types/react-reconciler` are outdated
+ scheduleMicrotask,
+
+ // Compat for React <= 17.x
+ queueMicrotask: scheduleMicrotask,
isPrimaryRenderer: true,
supportsMutation: true,
diff --git a/packages/react/src/render.tsx b/packages/react/src/render.tsx
index d476420d..9a68a3f8 100644
--- a/packages/react/src/render.tsx
+++ b/packages/react/src/render.tsx
@@ -1,4 +1,5 @@
import type {ReactElement} from 'react';
+import {version} from 'react';
import type {RemoteRoot} from '@remote-ui/core';
import type {RootTag} from 'react-reconciler';
@@ -15,8 +16,8 @@ const cache = new WeakMap<
}
>();
-// @see https://github.com/facebook/react/blob/993ca533b42756811731f6b7791ae06a35ee6b4d/packages/react-reconciler/src/ReactRootTags.js
-// I think we are a legacy root?
+// @see https://github.com/facebook/react/blob/fea6f8da6ab669469f2fa3f18bd3a831f00ab284/packages/react-reconciler/src/ReactRootTags.js#L12
+// We don't support concurrent rendering for now.
const LEGACY_ROOT: RootTag = 0;
export function render(
@@ -28,9 +29,26 @@ export function render(
let cached = cache.get(root);
if (!cached) {
+ const major = Number(version.split('.')?.[0] || 18);
+
// Since we haven't created a container for this root yet, create a new one
const value = {
- container: reconciler.createContainer(root, LEGACY_ROOT, false, null),
+ container:
+ major >= 18
+ ? reconciler.createContainer(
+ root,
+ LEGACY_ROOT,
+ null,
+ false,
+ null,
+ // Might not be necessary
+ 'r-ui',
+ () => null,
+ null,
+ )
+ : // eslint-disable-next-line @typescript-eslint/ban-ts-comment
+ // @ts-ignore - TS doesn't support multiple type versions of the same package
+ reconciler.createContainer(root, LEGACY_ROOT, false, null),
// We also cache the render context to avoid re-creating it on subsequent render calls
renderContext: {root, reconciler},
};
diff --git a/packages/react/src/tests/e2e.test.tsx b/packages/react/src/tests/e2e.test.tsx
index c727fa03..ceacbbf5 100644
--- a/packages/react/src/tests/e2e.test.tsx
+++ b/packages/react/src/tests/e2e.test.tsx
@@ -1,7 +1,8 @@
/* eslint @shopify/jsx-no-hardcoded-content: off */
import {useEffect, useContext, createContext, useState} from 'react';
-import {render as domRender} from 'react-dom';
+import {createRoot} from 'react-dom/client';
+import type {Root} from 'react-dom/client';
import {act as domAct, Simulate} from 'react-dom/test-utils';
import {
KIND_ROOT,
@@ -18,6 +19,10 @@ import {
ReactPropsFromRemoteComponentType,
} from '..';
+// Tell react to enable `act()` behaviour. See:
+// https://reactjs.org/blog/2022/03/08/react-18-upgrade-guide.html#configuring-your-testing-environment
+(globalThis as any).IS_REACT_ACT_ENVIRONMENT = true;
+
const RemoteHelloWorld = createRemoteReactComponent<
'HelloWorld',
{name: string | RemoteFragment}
@@ -97,14 +102,21 @@ function HostWithFragment({
describe('@remote-ui/react', () => {
let appElement!: HTMLElement;
+ let domRoot: Root;
beforeEach(() => {
appElement = document.createElement('div');
document.body.appendChild(appElement);
+ domAct(() => {
+ domRoot = createRoot(appElement);
+ });
jest.useFakeTimers();
});
afterEach(() => {
+ domAct(() => {
+ domRoot.unmount();
+ });
appElement.remove();
jest.useRealTimers();
});
@@ -128,7 +140,7 @@ describe('@remote-ui/react', () => {
}
domAct(() => {
- domRender(, appElement);
+ domRoot.render();
render(, remoteRoot, () => {
remoteRoot.mount();
});
@@ -168,7 +180,7 @@ describe('@remote-ui/react', () => {
}
domAct(() => {
- domRender(, appElement);
+ domRoot.render();
render(, remoteRoot, () => {
remoteRoot.mount();
});
@@ -200,11 +212,10 @@ describe('@remote-ui/react', () => {
}
domAct(() => {
- domRender(
+ domRoot.render(
,
- appElement,
);
render(, remoteRoot, () => {
remoteRoot.mount();
@@ -234,7 +245,7 @@ describe('@remote-ui/react', () => {
}
domAct(() => {
- domRender(, appElement);
+ domRoot.render();
render(, remoteRoot, () => {
remoteRoot.mount();
});
@@ -262,7 +273,7 @@ describe('@remote-ui/react', () => {
}
domAct(() => {
- domRender(, appElement);
+ domRoot.render();
render(, remoteRoot, () => {
remoteRoot.mount();
});
@@ -317,7 +328,7 @@ describe('@remote-ui/react', () => {
}
domAct(() => {
- domRender(, appElement);
+ domRoot.render();
render(, remoteRoot, () => {
remoteRoot.mount();
});
@@ -388,7 +399,7 @@ describe('@remote-ui/react', () => {
}
domAct(() => {
- domRender(, appElement);
+ domRoot.render();
render(, remoteRoot, () => {
remoteRoot.mount();
});
@@ -457,7 +468,7 @@ describe('@remote-ui/react', () => {
}
domAct(() => {
- domRender(, appElement);
+ domRoot.render();
render(, remoteRoot, () => {
remoteRoot.mount();
});
@@ -519,7 +530,7 @@ describe('@remote-ui/react', () => {
}
domAct(() => {
- domRender(, appElement);
+ domRoot.render();
render(, remoteRoot, () => {
remoteRoot.mount();
});
@@ -581,7 +592,7 @@ describe('@remote-ui/react', () => {
}
domAct(() => {
- domRender(, appElement);
+ domRoot.render();
render(, remoteRoot, () => {
remoteRoot.mount();
});
@@ -641,7 +652,7 @@ describe('@remote-ui/react', () => {
}
domAct(() => {
- domRender(, appElement);
+ domRoot.render();
render(, remoteRoot, () => {
remoteRoot.mount();
});
@@ -708,7 +719,7 @@ describe('@remote-ui/react', () => {
}
domAct(() => {
- domRender(, appElement);
+ domRoot.render();
render(, remoteRoot, () => {
remoteRoot.mount();
});
@@ -777,7 +788,7 @@ describe('@remote-ui/react', () => {
}
domAct(() => {
- domRender(, appElement);
+ domRoot.render();
render(, remoteRoot, () => {
remoteRoot.mount();
});
@@ -833,7 +844,7 @@ describe('@remote-ui/react', () => {
}
domAct(() => {
- domRender(, appElement);
+ domRoot.render();
render(, remoteRoot, () => {
remoteRoot.mount();
});
diff --git a/yarn.lock b/yarn.lock
index 058a8ff5..e6d9a02a 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -2747,17 +2747,17 @@
resolved "https://registry.yarnpkg.com/@types/prop-types/-/prop-types-15.7.3.tgz#2ab0d5da2e5815f94b0b9d4b95d1e5f243ab2ca7"
integrity sha512-KfRL3PuHmqQLOG+2tGpRO26Ctg+Cq1E01D2DMriKEATHgWLfeNDmq9e29Q9WIky0dQ3NPkd1mzYH8Lm936Z9qw==
-"@types/react-dom@^17.0.0":
- version "17.0.1"
- resolved "https://registry.yarnpkg.com/@types/react-dom/-/react-dom-17.0.1.tgz#d92d77d020bfb083e07cc8e0ac9f933599a4d56a"
- integrity sha512-yIVyopxQb8IDZ7SOHeTovurFq+fXiPICa+GV3gp0Xedsl+MwQlMLKmvrnEjFbQxjliH5YVAEWFh975eVNmKj7Q==
+"@types/react-dom@^18.0.5":
+ version "18.0.5"
+ resolved "https://registry.yarnpkg.com/@types/react-dom/-/react-dom-18.0.5.tgz#330b2d472c22f796e5531446939eacef8378444a"
+ integrity sha512-OWPWTUrY/NIrjsAPkAk1wW9LZeIjSvkXRhclsFO8CZcZGCOg2G0YZy4ft+rOyYxy8B7ui5iZzi9OkDebZ7/QSA==
dependencies:
"@types/react" "*"
-"@types/react-reconciler@^0.26.0":
- version "0.26.0"
- resolved "https://registry.yarnpkg.com/@types/react-reconciler/-/react-reconciler-0.26.0.tgz#fee923cb6621e6817ab4b84ad0f64707e4670a07"
- integrity sha512-kWnVUzDqUkvibnYaLGH251qE9+HHhklA18Fuj556dvbb4atzPDrU2F3+Xpmlt4NuoEwYbWLJSnk/YKGeqN9Acg==
+"@types/react-reconciler@^0.26.7":
+ version "0.26.7"
+ resolved "https://registry.yarnpkg.com/@types/react-reconciler/-/react-reconciler-0.26.7.tgz#0c4643f30821ae057e401b0d9037e03e8e9b2a36"
+ integrity sha512-mBDYl8x+oyPX/VBb3E638N0B7xG+SPk/EAMcVPeexqus/5aTpTphQi0curhhshOqRrc9t6OPoJfEUkbymse/lQ==
dependencies:
"@types/react" "*"
@@ -2768,7 +2768,7 @@
dependencies:
"@types/react" "*"
-"@types/react@*", "@types/react@>=17.0.0 <18.0.0":
+"@types/react@*":
version "17.0.14"
resolved "https://registry.yarnpkg.com/@types/react/-/react-17.0.14.tgz#f0629761ca02945c4e8fea99b8177f4c5c61fb0f"
integrity sha512-0WwKHUbWuQWOce61UexYuWTGuGY/8JvtUe/dtQ6lR4sZ3UiylHotJeWpf3ArP9+DSGUoLY3wbU59VyMrJps5VQ==
@@ -2777,6 +2777,15 @@
"@types/scheduler" "*"
csstype "^3.0.2"
+"@types/react@^18.0.9":
+ version "18.0.9"
+ resolved "https://registry.yarnpkg.com/@types/react/-/react-18.0.9.tgz#d6712a38bd6cd83469603e7359511126f122e878"
+ integrity sha512-9bjbg1hJHUm4De19L1cHiW0Jvx3geel6Qczhjd0qY5VKVE2X5+x77YxAepuCwVh4vrgZJdgEJw48zrhRIeF4Nw==
+ dependencies:
+ "@types/prop-types" "*"
+ "@types/scheduler" "*"
+ csstype "^3.0.2"
+
"@types/resolve@1.17.1":
version "1.17.1"
resolved "https://registry.yarnpkg.com/@types/resolve/-/resolve-1.17.1.tgz#3afd6ad8967c77e4376c598a82ddd58f46ec45d6"
@@ -8794,14 +8803,13 @@ quick-lru@^4.0.1:
resolved "https://registry.yarnpkg.com/quick-lru/-/quick-lru-4.0.1.tgz#5b8878f113a58217848c6482026c73e1ba57727f"
integrity sha512-ARhCpm70fzdcvNQfPoy49IaanKkTlRWF2JMzqhcJbhSFRZv7nPTvZJdcY7301IPmvW+/p0RgIWnQDLJxifsQ7g==
-react-dom@^17.0.0:
- version "17.0.1"
- resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-17.0.1.tgz#1de2560474ec9f0e334285662ede52dbc5426fc6"
- integrity sha512-6eV150oJZ9U2t9svnsspTMrWNyHc6chX0KzDeAOXftRa8bNeOKTTfCJ7KorIwenkHd2xqVTBTCZd79yk/lx/Ug==
+react-dom@^18.1.0:
+ version "18.1.0"
+ resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-18.1.0.tgz#7f6dd84b706408adde05e1df575b3a024d7e8a2f"
+ integrity sha512-fU1Txz7Budmvamp7bshe4Zi32d0ll7ect+ccxNu9FlObT605GOEB8BfO4tmRJ39R5Zj831VCpvQ05QPBW5yb+w==
dependencies:
loose-envify "^1.1.0"
- object-assign "^4.1.1"
- scheduler "^0.20.1"
+ scheduler "^0.22.0"
"react-is@^16.12.0 || ^17.0.0", react-is@^17.0.1, react-is@^17.0.2:
version "17.0.2"
@@ -8813,7 +8821,7 @@ react-is@^16.8.1:
resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.13.1.tgz#789729a4dc36de2999dc156dd6c1d9c18cea56a4"
integrity sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==
-"react-reconciler@>=0.26.0 <0.27.0", react-reconciler@^0.26.0:
+react-reconciler@^0.26.0:
version "0.26.2"
resolved "https://registry.yarnpkg.com/react-reconciler/-/react-reconciler-0.26.2.tgz#bbad0e2d1309423f76cf3c3309ac6c96e05e9d91"
integrity sha512-nK6kgY28HwrMNwDnMui3dvm3rCFjZrcGiuwLc5COUipBK5hWHLOxMJhSnSomirqWwjPBJKV1QcbkI0VJr7Gl1Q==
@@ -8822,6 +8830,14 @@ react-is@^16.8.1:
object-assign "^4.1.1"
scheduler "^0.20.2"
+react-reconciler@^0.28.0:
+ version "0.28.0"
+ resolved "https://registry.yarnpkg.com/react-reconciler/-/react-reconciler-0.28.0.tgz#52ede33c584c9b6d4c9bea02a53d368c5751c9c9"
+ integrity sha512-sGIHDOpgVjRYgsi8NgosDnbkDvvkYFFSF900ZUhUw0+lSBEA5n76TcKFaVkfYMIuYm+7W6mT8Q673DLBfuTxcQ==
+ dependencies:
+ loose-envify "^1.1.0"
+ scheduler "^0.22.0"
+
react-shallow-renderer@^16.13.1:
version "16.14.1"
resolved "https://registry.yarnpkg.com/react-shallow-renderer/-/react-shallow-renderer-16.14.1.tgz#bf0d02df8a519a558fd9b8215442efa5c840e124"
@@ -8840,13 +8856,12 @@ react-test-renderer@^17.0.0:
react-shallow-renderer "^16.13.1"
scheduler "^0.20.2"
-react@^17.0.0:
- version "17.0.1"
- resolved "https://registry.yarnpkg.com/react/-/react-17.0.1.tgz#6e0600416bd57574e3f86d92edba3d9008726127"
- integrity sha512-lG9c9UuMHdcAexXtigOZLX8exLWkW0Ku29qPRU8uhF2R9BN96dLCt0psvzPLlHc5OWkgymP3qwTRgbnw5BKx3w==
+react@^18.1.0:
+ version "18.1.0"
+ resolved "https://registry.yarnpkg.com/react/-/react-18.1.0.tgz#6f8620382decb17fdc5cc223a115e2adbf104890"
+ integrity sha512-4oL8ivCz5ZEPyclFQXaNksK3adutVS8l2xzZU0cqEFrE9Sb7fC0EFK5uEk74wIreL1DERyjvsU915j1pcT2uEQ==
dependencies:
loose-envify "^1.1.0"
- object-assign "^4.1.1"
read-cmd-shim@^1.0.1:
version "1.0.5"
@@ -9299,7 +9314,7 @@ saxes@^5.0.1:
dependencies:
xmlchars "^2.2.0"
-scheduler@^0.20.1, scheduler@^0.20.2:
+scheduler@^0.20.2:
version "0.20.2"
resolved "https://registry.yarnpkg.com/scheduler/-/scheduler-0.20.2.tgz#4baee39436e34aa93b4874bddcbf0fe8b8b50e91"
integrity sha512-2eWfGgAqqWFGqtdMmcL5zCMK1U8KlXv8SQFGglL3CEtd0aDVDWgeF/YoCmvln55m5zSk3J/20hTaSBeSObsQDQ==
@@ -9307,6 +9322,13 @@ scheduler@^0.20.1, scheduler@^0.20.2:
loose-envify "^1.1.0"
object-assign "^4.1.1"
+scheduler@^0.22.0:
+ version "0.22.0"
+ resolved "https://registry.yarnpkg.com/scheduler/-/scheduler-0.22.0.tgz#83a5d63594edf074add9a7198b1bae76c3db01b8"
+ integrity sha512-6QAm1BgQI88NPYymgGQLCZgvep4FyePDWFpXVK+zNSUgHwlqpJy8VEh8Et0KxTACS4VWwMousBElAZOH9nkkoQ==
+ dependencies:
+ loose-envify "^1.1.0"
+
secure-compare@3.0.1:
version "3.0.1"
resolved "https://registry.yarnpkg.com/secure-compare/-/secure-compare-3.0.1.tgz#f1a0329b308b221fae37b9974f3d578d0ca999e3"