From c900d58d33514f9881cd154938218eec458e4d12 Mon Sep 17 00:00:00 2001 From: Marvin Hagemeister Date: Fri, 27 May 2022 14:25:08 +0200 Subject: [PATCH 1/2] Add support for React 18 --- packages/react/package.json | 12 ++--- packages/react/src/reconciler.ts | 20 ++++++-- packages/react/src/render.tsx | 24 ++++++++-- packages/react/src/tests/e2e.test.tsx | 29 ++++++++---- yarn.lock | 66 ++++++++++++++++++--------- 5 files changed, 108 insertions(+), 43 deletions(-) diff --git a/packages/react/package.json b/packages/react/package.json index cbf19bb6..ef7b77cb 100644 --- a/packages/react/package.json +++ b/packages/react/package.json @@ -32,16 +32,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": { "@remote-ui/async-subscription": "^2.1.9", "@remote-ui/core": "^2.1.9", "@remote-ui/rpc": "^1.3.0", - "@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 8449a906..6113615a 100644 --- a/packages/react/src/tests/e2e.test.tsx +++ b/packages/react/src/tests/e2e.test.tsx @@ -1,5 +1,6 @@ import {useEffect, useContext, createContext} 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} from 'react-dom/test-utils'; import { KIND_ROOT, @@ -16,6 +17,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} @@ -74,14 +79,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(); }); @@ -105,7 +117,7 @@ describe('@remote-ui/react', () => { } domAct(() => { - domRender(, appElement); + domRoot.render(); render(, remoteRoot, () => { remoteRoot.mount(); }); @@ -145,7 +157,7 @@ describe('@remote-ui/react', () => { } domAct(() => { - domRender(, appElement); + domRoot.render(); render(, remoteRoot, () => { remoteRoot.mount(); }); @@ -177,11 +189,10 @@ describe('@remote-ui/react', () => { } domAct(() => { - domRender( + domRoot.render( , - appElement, ); render(, remoteRoot, () => { remoteRoot.mount(); @@ -211,7 +222,7 @@ describe('@remote-ui/react', () => { } domAct(() => { - domRender(, appElement); + domRoot.render(); render(, remoteRoot, () => { remoteRoot.mount(); }); @@ -239,7 +250,7 @@ describe('@remote-ui/react', () => { } domAct(() => { - domRender(, appElement); + domRoot.render(); render(, remoteRoot, () => { remoteRoot.mount(); }); @@ -291,7 +302,7 @@ describe('@remote-ui/react', () => { } domAct(() => { - domRender(, appElement); + domRoot.render(); render(, remoteRoot, () => { remoteRoot.mount(); }); @@ -358,7 +369,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" From 4a784284a196f6f2754f797c5488d50007e6931f Mon Sep 17 00:00:00 2001 From: Bart van den Aardweg Date: Wed, 16 Nov 2022 12:39:13 +0100 Subject: [PATCH 2/2] fix(react): tests render syntax --- packages/react/src/tests/e2e.test.tsx | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/packages/react/src/tests/e2e.test.tsx b/packages/react/src/tests/e2e.test.tsx index d0f7a580..ceacbbf5 100644 --- a/packages/react/src/tests/e2e.test.tsx +++ b/packages/react/src/tests/e2e.test.tsx @@ -2,7 +2,7 @@ import {useEffect, useContext, createContext, useState} from 'react'; import {createRoot} from 'react-dom/client'; -import type {Root} from 'react-dom/client'; +import type {Root} from 'react-dom/client'; import {act as domAct, Simulate} from 'react-dom/test-utils'; import { KIND_ROOT, @@ -328,7 +328,7 @@ describe('@remote-ui/react', () => { } domAct(() => { - domRender(, appElement); + domRoot.render(); render(, remoteRoot, () => { remoteRoot.mount(); }); @@ -399,7 +399,7 @@ describe('@remote-ui/react', () => { } domAct(() => { - domRender(, appElement); + domRoot.render(); render(, remoteRoot, () => { remoteRoot.mount(); }); @@ -468,7 +468,7 @@ describe('@remote-ui/react', () => { } domAct(() => { - domRender(, appElement); + domRoot.render(); render(, remoteRoot, () => { remoteRoot.mount(); }); @@ -530,7 +530,7 @@ describe('@remote-ui/react', () => { } domAct(() => { - domRender(, appElement); + domRoot.render(); render(, remoteRoot, () => { remoteRoot.mount(); }); @@ -592,7 +592,7 @@ describe('@remote-ui/react', () => { } domAct(() => { - domRender(, appElement); + domRoot.render(); render(, remoteRoot, () => { remoteRoot.mount(); }); @@ -788,7 +788,7 @@ describe('@remote-ui/react', () => { } domAct(() => { - domRender(, appElement); + domRoot.render(); render(, remoteRoot, () => { remoteRoot.mount(); }); @@ -844,7 +844,7 @@ describe('@remote-ui/react', () => { } domAct(() => { - domRender(, appElement); + domRoot.render(); render(, remoteRoot, () => { remoteRoot.mount(); });