diff --git a/ts/background.ts b/ts/background.ts index 025e10d74c..e454b8b59c 100644 --- a/ts/background.ts +++ b/ts/background.ts @@ -2,7 +2,7 @@ // SPDX-License-Identifier: AGPL-3.0-only import { isNumber, groupBy, throttle } from 'lodash'; -import { render } from 'react-dom'; +import { createRoot } from 'react-dom/client'; import PQueue from 'p-queue'; import pMap from 'p-map'; import { v7 as generateUuid } from 'uuid'; @@ -1552,9 +1552,11 @@ export async function startApp(): Promise { await runAllSyncTasks(); cancelInitializationMessage(); - render( - window.Signal.State.Roots.createApp(window.reduxStore), - document.getElementById('app-container') + + const appContainer = document.getElementById('app-container'); + strictAssert(appContainer != null, 'No #app-container'); + createRoot(appContainer).render( + window.Signal.State.Roots.createApp(window.reduxStore) ); const hideMenuBar = window.storage.get('hide-menu-bar', false); window.IPC.setAutoHideMenuBar(hideMenuBar); diff --git a/ts/groups/joinViaLink.ts b/ts/groups/joinViaLink.ts index e6833101d8..8da1518c9a 100644 --- a/ts/groups/joinViaLink.ts +++ b/ts/groups/joinViaLink.ts @@ -1,7 +1,8 @@ // Copyright 2021 Signal Messenger, LLC // SPDX-License-Identifier: AGPL-3.0-only -import { render, unmountComponentAtNode } from 'react-dom'; +import { unmountComponentAtNode } from 'react-dom'; +import { createRoot } from 'react-dom/client'; import type { ConversationAttributesType } from '../model-types.d'; import type { ConversationModel } from '../models/conversations'; @@ -389,9 +390,8 @@ export async function joinViaLink(value: string): Promise { let groupV2InfoNode: HTMLDivElement | undefined = document.createElement('div'); - render( - createGroupV2JoinModal(window.reduxStore, { join, onClose: closeDialog }), - groupV2InfoNode + createRoot(groupV2InfoNode).render( + createGroupV2JoinModal(window.reduxStore, { join, onClose: closeDialog }) ); // We declare a new function here so we can await but not block diff --git a/ts/quill/mentions/blot.tsx b/ts/quill/mentions/blot.tsx index f2e0d4ab69..5846e5f11a 100644 --- a/ts/quill/mentions/blot.tsx +++ b/ts/quill/mentions/blot.tsx @@ -3,7 +3,7 @@ import React, { StrictMode } from 'react'; import EmbedBlot from '@signalapp/quill-cjs/blots/embed'; -import { render } from 'react-dom'; +import { createRoot } from 'react-dom/client'; import { Emojify } from '../../components/conversation/Emojify'; import { normalizeAci } from '../../util/normalizeAci'; @@ -46,7 +46,7 @@ export class MentionBlot extends EmbedBlot { const mentionSpan = document.createElement('span'); - render( + createRoot(mentionSpan).render( @@ -56,8 +56,7 @@ export class MentionBlot extends EmbedBlot { - , - mentionSpan + ); node.appendChild(mentionSpan); diff --git a/ts/shims/renderClearingDataView.tsx b/ts/shims/renderClearingDataView.tsx index 3a3d142852..6684ff6af0 100644 --- a/ts/shims/renderClearingDataView.tsx +++ b/ts/shims/renderClearingDataView.tsx @@ -2,19 +2,21 @@ // SPDX-License-Identifier: AGPL-3.0-only import React, { StrictMode } from 'react'; -import { render } from 'react-dom'; +import { createRoot } from 'react-dom/client'; import { ClearingData } from '../components/ClearingData'; +import { strictAssert } from '../util/assert'; import { deleteAllData } from './deleteAllData'; import { FunDefaultEnglishEmojiLocalizationProvider } from '../components/fun/FunEmojiLocalizationProvider'; export function renderClearingDataView(): void { - render( + const appContainer = document.getElementById('app-container'); + strictAssert(appContainer != null, 'No #app-container'); + createRoot(appContainer).render( - , - document.getElementById('app-container') + ); } diff --git a/ts/util/longRunningTaskWrapper.tsx b/ts/util/longRunningTaskWrapper.tsx index 937d68c4ac..858fc3152a 100644 --- a/ts/util/longRunningTaskWrapper.tsx +++ b/ts/util/longRunningTaskWrapper.tsx @@ -2,7 +2,8 @@ // SPDX-License-Identifier: AGPL-3.0-only import React, { StrictMode } from 'react'; -import { render, unmountComponentAtNode } from 'react-dom'; +import { unmountComponentAtNode } from 'react-dom'; +import { createRoot } from 'react-dom/client'; import * as Errors from '../types/errors'; import { createLogger } from '../logging/log'; @@ -34,13 +35,12 @@ export async function longRunningTaskWrapper({ progressNode = document.createElement('div'); log.info(`${idLog}: Creating spinner`); - render( + createRoot(progressNode).render( - , - progressNode + ); spinnerStart = Date.now(); }, TWO_SECONDS); diff --git a/ts/util/showConfirmationDialog.tsx b/ts/util/showConfirmationDialog.tsx index 7e9564fed8..6fdd6877f2 100644 --- a/ts/util/showConfirmationDialog.tsx +++ b/ts/util/showConfirmationDialog.tsx @@ -2,7 +2,8 @@ // SPDX-License-Identifier: AGPL-3.0-only import React, { StrictMode } from 'react'; -import { render, unmountComponentAtNode } from 'react-dom'; +import { unmountComponentAtNode } from 'react-dom'; +import { createRoot } from 'react-dom/client'; import { ConfirmationDialog } from '../components/ConfirmationDialog'; import { FunDefaultEnglishEmojiLocalizationProvider } from '../components/fun/FunEmojiLocalizationProvider'; @@ -55,7 +56,7 @@ export function showConfirmationDialog( confirmationDialogPreviousFocus = document.activeElement as HTMLElement; - render( + createRoot(confirmationDialogViewNode).render( - , - confirmationDialogViewNode + ); } diff --git a/ts/windows/about/app.tsx b/ts/windows/about/app.tsx index 2c4feb2646..8c024be739 100644 --- a/ts/windows/about/app.tsx +++ b/ts/windows/about/app.tsx @@ -2,7 +2,7 @@ // SPDX-License-Identifier: AGPL-3.0-only import React, { StrictMode } from 'react'; -import ReactDOM from 'react-dom'; +import { createRoot } from 'react-dom/client'; import { About } from '../../components/About'; import { i18n } from '../sandboxedInit'; @@ -13,7 +13,10 @@ const { AboutWindowProps } = window.Signal; strictAssert(AboutWindowProps, 'window values not provided'); -ReactDOM.render( +const app = document.getElementById('app'); +strictAssert(app != null, 'No #app'); + +createRoot(app).render( - , - document.getElementById('app') + ); diff --git a/ts/windows/debuglog/app.tsx b/ts/windows/debuglog/app.tsx index 3483403641..caf8d63f20 100644 --- a/ts/windows/debuglog/app.tsx +++ b/ts/windows/debuglog/app.tsx @@ -2,7 +2,7 @@ // SPDX-License-Identifier: AGPL-3.0-only import React, { StrictMode } from 'react'; -import { render } from 'react-dom'; +import { createRoot } from 'react-dom/client'; import { DebugLogWindow } from '../../components/DebugLogWindow'; import { FunDefaultEnglishEmojiLocalizationProvider } from '../../components/fun/FunEmojiLocalizationProvider'; import { i18n } from '../sandboxedInit'; @@ -12,7 +12,10 @@ const { DebugLogWindowProps } = window.Signal; strictAssert(DebugLogWindowProps, 'window values not provided'); -render( +const app = document.getElementById('app'); +strictAssert(app != null, 'No #app'); + +createRoot(app).render( - , - document.getElementById('app') + ); diff --git a/ts/windows/main/phase2-dependencies.ts b/ts/windows/main/phase2-dependencies.ts index 088f6619d6..e6af85308c 100644 --- a/ts/windows/main/phase2-dependencies.ts +++ b/ts/windows/main/phase2-dependencies.ts @@ -3,8 +3,6 @@ import Backbone from 'backbone'; import { PhoneNumberUtil, PhoneNumberFormat } from 'google-libphonenumber'; -import * as React from 'react'; -import * as ReactDOM from 'react-dom'; import * as moment from 'moment'; // @ts-expect-error -- no types import 'moment/min/locales.min'; @@ -49,9 +47,6 @@ window.WebAPI = window.textsecure.WebAPI.initialize({ window.libphonenumberInstance = PhoneNumberUtil.getInstance(); window.libphonenumberFormat = PhoneNumberFormat; -window.React = React; -window.ReactDOM = ReactDOM; - const { resolvedTranslationsLocale, preferredSystemLocales, localeOverride } = config; diff --git a/ts/windows/permissions/app.tsx b/ts/windows/permissions/app.tsx index 466a54363c..e5d57f3dcc 100644 --- a/ts/windows/permissions/app.tsx +++ b/ts/windows/permissions/app.tsx @@ -2,7 +2,7 @@ // SPDX-License-Identifier: AGPL-3.0-only import React, { StrictMode } from 'react'; -import ReactDOM from 'react-dom'; +import { createRoot } from 'react-dom/client'; import { PermissionsPopup } from '../../components/PermissionsPopup'; import { i18n } from '../sandboxedInit'; @@ -26,7 +26,10 @@ if (forCalling) { message = i18n('icu:audioPermissionNeeded'); } -ReactDOM.render( +const app = document.getElementById('app'); +strictAssert(app != null, 'No #app'); + +createRoot(app).render( - , - document.getElementById('app') + ); diff --git a/ts/windows/screenShare/app.tsx b/ts/windows/screenShare/app.tsx index 8ab18e7d1a..18152c33ca 100644 --- a/ts/windows/screenShare/app.tsx +++ b/ts/windows/screenShare/app.tsx @@ -2,7 +2,7 @@ // SPDX-License-Identifier: AGPL-3.0-only import React, { StrictMode } from 'react'; -import ReactDOM from 'react-dom'; +import { createRoot } from 'react-dom/client'; import { CallingScreenSharingController } from '../../components/CallingScreenSharingController'; import { i18n } from '../sandboxedInit'; @@ -28,7 +28,10 @@ function render() { // Pacify typescript strictAssert(ScreenShareWindowProps, 'window values not provided'); - ReactDOM.render( + const app = document.getElementById('app'); + strictAssert(app != null, 'No #app'); + + createRoot(app).render(
@@ -41,9 +44,7 @@ function render() { />
-
, - - document.getElementById('app') + ); } render();