Update rendering to use non depricated react-dom calls.

This commit is contained in:
Axel Wretman
2025-09-09 16:16:49 +02:00
parent 162eb5f6cb
commit 5bed0a3c17
11 changed files with 63 additions and 53 deletions

View File

@@ -1,7 +1,7 @@
import * as remote from '@electron/remote'; import * as remote from '@electron/remote';
import { ipcRenderer } from 'electron'; import { ipcRenderer } from 'electron';
import React from 'react'; import React from 'react';
import ReactDOM from 'react-dom'; import { createRoot } from 'react-dom/client';
import './process-setup'; import './process-setup';
@@ -83,5 +83,5 @@ window.addEventListener('DOMContentLoaded', () => {
// Create the main element // Create the main element
const rootElement = document.getElementById('root'); const rootElement = document.getElementById('root');
ReactDOM.render(React.createElement(Router, { uri: remote.process.env.noodlURI }), rootElement); createRoot(rootElement).render(React.createElement(Router, { uri: remote.process.env.noodlURI }));
}); });

View File

@@ -3,7 +3,7 @@ import { useEffect, useRef } from 'react';
type IntervalCallback = () => Promise<void>; type IntervalCallback = () => Promise<void>;
export function useInterval(callback: IntervalCallback, delay: number) { export function useInterval(callback: IntervalCallback, delay: number) {
const savedCallback = useRef<IntervalCallback>(); const savedCallback = useRef<IntervalCallback>(callback);
// Remember the latest callback. // Remember the latest callback.
useEffect(() => { useEffect(() => {

View File

@@ -1,6 +1,6 @@
import { ipcRenderer } from 'electron'; import { ipcRenderer } from 'electron';
import React from 'react'; import React from 'react';
import ReactDOM from 'react-dom'; import { createRoot } from 'react-dom/client';
import { EventDispatcher } from '../../shared/utils/EventDispatcher'; import { EventDispatcher } from '../../shared/utils/EventDispatcher';
import LessonTemplatesModel from './models/lessontemplatesmodel'; import LessonTemplatesModel from './models/lessontemplatesmodel';
@@ -24,11 +24,11 @@ function createToastLayer() {
toastLayer.classList.add('toast-layer'); toastLayer.classList.add('toast-layer');
$('body').append(toastLayer); $('body').append(toastLayer);
ReactDOM.render(React.createElement(ToastLayerContainer), toastLayer); createRoot(toastLayer).render(React.createElement(ToastLayerContainer));
if (import.meta.webpackHot) { if (import.meta.webpackHot) {
import.meta.webpackHot.accept('./views/ToastLayer', () => { import.meta.webpackHot.accept('./views/ToastLayer', () => {
ReactDOM.render(React.createElement(ToastLayerContainer), toastLayer); createRoot(toastLayer).render(React.createElement(ToastLayerContainer));
}); });
} }
} }
@@ -47,11 +47,11 @@ function createDialogLayer() {
dialogLayer.classList.add('dialog-layer'); dialogLayer.classList.add('dialog-layer');
$('body').append(dialogLayer); $('body').append(dialogLayer);
ReactDOM.render(React.createElement(DialogLayerContainer), dialogLayer); createRoot(dialogLayer).render(React.createElement(DialogLayerContainer));
if (import.meta.webpackHot) { if (import.meta.webpackHot) {
import.meta.webpackHot.accept('./views/DialogLayer', () => { import.meta.webpackHot.accept('./views/DialogLayer', () => {
ReactDOM.render(React.createElement(DialogLayerContainer), dialogLayer); createRoot(dialogLayer).render(React.createElement(DialogLayerContainer));
}); });
} }
} }

View File

@@ -51,7 +51,7 @@ export default function Clippy() {
const firstInputRef = useRef(null); const firstInputRef = useRef(null);
const secondInputRef = useRef(null); const secondInputRef = useRef(null);
const secondTextAreaRef = useRef(null); const secondTextAreaRef = useRef(null);
const ref = useRef<HTMLDivElement>(); const ref = useRef<HTMLDivElement>(undefined);
const [hasApiKey, setHasApiKey] = useState(false); const [hasApiKey, setHasApiKey] = useState(false);
const aiAssistantModel = useModernModel(AiAssistantModel.instance); const aiAssistantModel = useModernModel(AiAssistantModel.instance);
const nodeGraphContext = useNodeGraphContext(); const nodeGraphContext = useNodeGraphContext();

View File

@@ -1,6 +1,6 @@
import { getCurrentWindow, screen } from '@electron/remote'; import { getCurrentWindow, screen } from '@electron/remote';
import React from 'react'; import React from 'react';
import ReactDOM from 'react-dom'; import { createRoot } from 'react-dom/client';
import { DialogRenderDirection } from '@noodl-core-ui/components/layout/BaseDialog'; import { DialogRenderDirection } from '@noodl-core-ui/components/layout/BaseDialog';
import { MenuDialog, MenuDialogItem, MenuDialogWidth } from '@noodl-core-ui/components/popups/MenuDialog'; import { MenuDialog, MenuDialogItem, MenuDialogWidth } from '@noodl-core-ui/components/popups/MenuDialog';
@@ -23,6 +23,7 @@ export function showContextMenuInPopup({
const container = document.createElement('div'); const container = document.createElement('div');
const screenPoint = screen.getCursorScreenPoint(); const screenPoint = screen.getCursorScreenPoint();
const [winX, winY] = getCurrentWindow().getPosition(); const [winX, winY] = getCurrentWindow().getPosition();
const root = createRoot(container)
const popout = PopupLayer.instance.showPopout({ const popout = PopupLayer.instance.showPopout({
content: { el: $(container) }, content: { el: $(container) },
@@ -33,11 +34,11 @@ export function showContextMenuInPopup({
}, },
position: 'top', position: 'top',
onClose: () => { onClose: () => {
ReactDOM.unmountComponentAtNode(container); root.unmount();
} }
}); });
ReactDOM.render( root.render(
<MenuDialog <MenuDialog
title={title} title={title}
width={width || MenuDialogWidth.Large} width={width || MenuDialogWidth.Large}
@@ -48,7 +49,5 @@ export function showContextMenuInPopup({
PopupLayer.instance.hidePopout(popout); PopupLayer.instance.hidePopout(popout);
}} }}
items={items} items={items}
/>, />);
container
);
} }

View File

@@ -1,6 +1,6 @@
import _ from 'underscore'; import _ from 'underscore';
import React from 'react'; import React from 'react';
import ReactDOM from 'react-dom'; import { createRoot, Root } from 'react-dom/client';
import { Comment, CommentsModel } from '@noodl-models/commentsmodel'; import { Comment, CommentsModel } from '@noodl-models/commentsmodel';
import KeyboardHandler from '@noodl-utils/keyboardhandler'; import KeyboardHandler from '@noodl-utils/keyboardhandler';
@@ -36,6 +36,8 @@ export default class CommentLayer {
backgroundDiv: HTMLDivElement; backgroundDiv: HTMLDivElement;
activeCommentId: string; activeCommentId: string;
foregroundDiv: HTMLDivElement; foregroundDiv: HTMLDivElement;
backgroundRoot: Root;
foregroundRoot: Root;
constructor(nodegraphEditor) { constructor(nodegraphEditor) {
this.nodegraphEditor = nodegraphEditor; this.nodegraphEditor = nodegraphEditor;
@@ -140,14 +142,16 @@ export default class CommentLayer {
return; return;
} }
ReactDOM.render(React.createElement(CommentLayerView.Background, this.props), this.backgroundDiv); this.backgroundRoot = createRoot(this.backgroundDiv);
ReactDOM.render(React.createElement(CommentLayerView.Foreground, this.props), this.foregroundDiv); this.backgroundRoot.render(React.createElement(CommentLayerView.Background, this.props));
this.foregroundRoot = createRoot(this.foregroundDiv);
this.foregroundRoot.render(React.createElement(CommentLayerView.Foreground, this.props));
} }
renderTo(backgroundDiv, foregroundDiv) { renderTo(backgroundDiv, foregroundDiv) {
if (this.backgroundDiv) { if (this.backgroundDiv) {
ReactDOM.unmountComponentAtNode(this.backgroundDiv); this.backgroundRoot.unmount();
ReactDOM.unmountComponentAtNode(this.foregroundDiv); this.foregroundRoot.unmount();
} }
this.backgroundDiv = backgroundDiv; this.backgroundDiv = backgroundDiv;
@@ -297,8 +301,8 @@ export default class CommentLayer {
} }
dispose() { dispose() {
ReactDOM.unmountComponentAtNode(this.foregroundDiv); this.foregroundRoot.unmount();
ReactDOM.unmountComponentAtNode(this.backgroundDiv); this.backgroundRoot.unmount();
//hack to remove all event listeners without having to keep track of them //hack to remove all event listeners without having to keep track of them
const newForegroundDiv = this.foregroundDiv.cloneNode(true); const newForegroundDiv = this.foregroundDiv.cloneNode(true);

View File

@@ -1,7 +1,7 @@
import { clipboard, ipcRenderer } from 'electron'; import { clipboard, ipcRenderer } from 'electron';
import _ from 'underscore'; import _ from 'underscore';
import React from 'react'; import React from 'react';
import ReactDOM from 'react-dom'; import { createRoot, Root } from 'react-dom/client';
import { NodeGraphColors } from '@noodl-constants/NodeGraphColors'; import { NodeGraphColors } from '@noodl-constants/NodeGraphColors';
import { AiAssistantEvent, AiAssistantModel } from '@noodl-models/AiAssistant/AiAssistantModel'; import { AiAssistantEvent, AiAssistantModel } from '@noodl-models/AiAssistant/AiAssistantModel';
@@ -227,6 +227,8 @@ export class NodeGraphEditor extends View {
nodesIdsAnimating: string[]; nodesIdsAnimating: string[];
isPlayingNodeAnimations: boolean; isPlayingNodeAnimations: boolean;
toolbarRoots: Root[];
constructor(args) { constructor(args) {
super(); super();
@@ -1418,8 +1420,8 @@ export class NodeGraphEditor extends View {
} }
updateTitle() { updateTitle() {
const root = this.el[0].querySelector('.nodegraph-component-trail-root'); const rootElem = this.el[0].querySelector('.nodegraph-component-trail-root');
const root = createRoot(rootElem);
if (this.activeComponent) { if (this.activeComponent) {
const fullName = this.activeComponent.fullName; const fullName = this.activeComponent.fullName;
const nameParts = fullName.split('/'); const nameParts = fullName.split('/');
@@ -1464,9 +1466,9 @@ export class NodeGraphEditor extends View {
canNavigateForward: this.navigationHistory.canNavigateForward canNavigateForward: this.navigationHistory.canNavigateForward
}; };
ReactDOM.render(React.createElement(NodeGraphComponentTrail, props), root); root.render(React.createElement(NodeGraphComponentTrail, props));
} else { } else {
ReactDOM.unmountComponentAtNode(root); root.unmount();
} }
} }
@@ -1761,10 +1763,10 @@ export class NodeGraphEditor extends View {
// @ts-expect-error // @ts-expect-error
toProps.sourcePort = fromPort; toProps.sourcePort = fromPort;
toProps.disabled = false; toProps.disabled = false;
ReactDOM.render(React.createElement(ConnectionPopup, toProps), toDiv); createRoot(toDiv).render(React.createElement(ConnectionPopup, toProps));
fromProps.disabled = true; fromProps.disabled = true;
ReactDOM.render(React.createElement(ConnectionPopup, fromProps), fromDiv); createRoot(fromDiv).render(React.createElement(ConnectionPopup, fromProps));
fromNode.borderHighlighted = false; fromNode.borderHighlighted = false;
toNode.borderHighlighted = true; toNode.borderHighlighted = true;
@@ -1772,7 +1774,8 @@ export class NodeGraphEditor extends View {
} }
}; };
const fromDiv = document.createElement('div'); const fromDiv = document.createElement('div');
ReactDOM.render(React.createElement(ConnectionPopup, fromProps), fromDiv); const root = createRoot(fromDiv);
root.render(React.createElement(ConnectionPopup, fromProps));
const fromPosition = toNode.global.x > fromNodeXPos ? 'left' : 'right'; const fromPosition = toNode.global.x > fromNodeXPos ? 'left' : 'right';
@@ -1790,7 +1793,7 @@ export class NodeGraphEditor extends View {
y: (fromNode.global.y + panAndScale.y) * panAndScale.scale + tl[1] + 20 * panAndScale.scale y: (fromNode.global.y + panAndScale.y) * panAndScale.scale + tl[1] + 20 * panAndScale.scale
}, },
onClose: () => { onClose: () => {
ReactDOM.unmountComponentAtNode(fromDiv); root.unmount();
ipcRenderer.send('viewer-show'); ipcRenderer.send('viewer-show');
} }
}); });
@@ -1824,10 +1827,10 @@ export class NodeGraphEditor extends View {
// @ts-expect-error // @ts-expect-error
toProps.sourcePort = undefined; toProps.sourcePort = undefined;
toProps.disabled = true; toProps.disabled = true;
ReactDOM.render(React.createElement(ConnectionPopup, toProps), toDiv); createRoot(toDiv).render(React.createElement(ConnectionPopup, toProps));
fromProps.disabled = false; fromProps.disabled = false;
ReactDOM.render(React.createElement(ConnectionPopup, fromProps), fromDiv); createRoot(fromDiv).render(React.createElement(ConnectionPopup, fromProps));
fromNode.borderHighlighted = true; fromNode.borderHighlighted = true;
toNode.borderHighlighted = false; toNode.borderHighlighted = false;
@@ -1836,7 +1839,7 @@ export class NodeGraphEditor extends View {
} }
}; };
const toDiv = document.createElement('div'); const toDiv = document.createElement('div');
ReactDOM.render(React.createElement(ConnectionPopup, toProps), toDiv); createRoot(toDiv).render(React.createElement(ConnectionPopup, toProps));
const toPosition = fromNodeXPos >= toNode.global.x ? 'left' : 'right'; const toPosition = fromNodeXPos >= toNode.global.x ? 'left' : 'right';
const toPopout = PopupLayer.instance.showPopout({ const toPopout = PopupLayer.instance.showPopout({
@@ -1851,7 +1854,7 @@ export class NodeGraphEditor extends View {
y: (toNode.global.y + panAndScale.y) * panAndScale.scale + tl[1] + 20 * panAndScale.scale y: (toNode.global.y + panAndScale.y) * panAndScale.scale + tl[1] + 20 * panAndScale.scale
}, },
onClose: () => { onClose: () => {
ReactDOM.unmountComponentAtNode(toDiv); root.unmount();
this.clearSelection(); this.clearSelection();
this.repaint(); this.repaint();
} }
@@ -2203,20 +2206,23 @@ export class NodeGraphEditor extends View {
div.style.position = 'absolute'; div.style.position = 'absolute';
div.style.left = pos.x + 'px'; div.style.left = pos.x + 'px';
div.style.top = pos.y + 'px'; div.style.top = pos.y + 'px';
const root = createRoot(div);
ReactDOM.render( this.toolbarRoots.push(root);
root.render(
React.createElement(PopupToolbar, { React.createElement(PopupToolbar, {
menuItems, menuItems,
contextMenuItems: this.getContextMenuActions() contextMenuItems: this.getContextMenuActions()
} as PopupToolbarProps), } as PopupToolbarProps)
div
); );
} }
hideNodeToolbar() { hideNodeToolbar() {
for (const root of this.toolbarRoots) {
root.unmount();
}
this.toolbarRoots = [];
const toolbars = this.domElementContainer.querySelectorAll('.nodegraph-node-toolbar'); const toolbars = this.domElementContainer.querySelectorAll('.nodegraph-node-toolbar');
for (const toolbar of toolbars) { for (const toolbar of toolbars) {
ReactDOM.unmountComponentAtNode(toolbar);
this.domElementContainer.removeChild(toolbar); this.domElementContainer.removeChild(toolbar);
} }
} }

View File

@@ -1,6 +1,6 @@
import classNames from 'classnames'; import classNames from 'classnames';
import React from 'react'; import React from 'react';
import ReactJson from 'react-json-view'; import ReactJson from '@microlink/react-json-view';
import { ProjectModel } from '@noodl-models/projectmodel'; import { ProjectModel } from '@noodl-models/projectmodel';

View File

@@ -1,5 +1,5 @@
import React, { useEffect, useRef, useState } from 'react'; import React, { useEffect, useRef, useState } from 'react';
import ReactDOM from 'react-dom'; import { createRoot } from 'react-dom/client';
import { Git } from '@noodl/git'; import { Git } from '@noodl/git';
import { platform } from '@noodl/platform'; import { platform } from '@noodl/platform';
@@ -126,8 +126,8 @@ function BaseVersionControlPanel() {
function openGitSettingsPopout() { function openGitSettingsPopout() {
const popoutDiv = document.createElement('div'); const popoutDiv = document.createElement('div');
const root = createRoot(popoutDiv);
ReactDOM.render(React.createElement(GitProviderPopout, { git }), popoutDiv); root.render(React.createElement(GitProviderPopout, { git }));
//the timeout is needed to solve a bug when the popout us opened from the git status button //the timeout is needed to solve a bug when the popout us opened from the git status button
//it causes timing issues between native events and react where the popout is instantly closed //it causes timing issues between native events and react where the popout is instantly closed
@@ -138,7 +138,7 @@ function BaseVersionControlPanel() {
position: 'right', position: 'right',
disableDynamicPositioning: true, disableDynamicPositioning: true,
onClose: () => { onClose: () => {
ReactDOM.unmountComponentAtNode(popoutDiv); root.unmount();
fetch.fetchRemote(); fetch.fetchRemote();
} }
}); });

View File

@@ -1,7 +1,7 @@
import { useModernModel } from '@noodl-hooks/useModel'; import { useModernModel } from '@noodl-hooks/useModel';
import { OpenAiStore } from '@noodl-store/AiAssistantStore'; import { OpenAiStore } from '@noodl-store/AiAssistantStore';
import React, { useEffect, useMemo, useRef, useState } from 'react'; import React, { useEffect, useMemo, useRef, useState } from 'react';
import ReactDOM from 'react-dom'; import { createRoot } from 'react-dom/client';
import { AiAssistantModel } from '@noodl-models/AiAssistant/AiAssistantModel'; import { AiAssistantModel } from '@noodl-models/AiAssistant/AiAssistantModel';
import { AiCopilotContext } from '@noodl-models/AiAssistant/AiCopilotContext'; import { AiCopilotContext } from '@noodl-models/AiAssistant/AiCopilotContext';
@@ -332,7 +332,8 @@ function AiMessageFunctionNodeAffix({ context, onUpdated }: AiMessageFunctionNod
}; };
const popoutDiv = document.createElement('div'); const popoutDiv = document.createElement('div');
ReactDOM.render(React.createElement(CodeEditor, props), popoutDiv); const root = createRoot(popoutDiv);
root.render(React.createElement(CodeEditor, props));
const popout = PopupLayer.instance.showPopout({ const popout = PopupLayer.instance.showPopout({
content: { el: [popoutDiv] }, content: { el: [popoutDiv] },

View File

@@ -1,11 +1,12 @@
import React from 'react';
import ReactDOM from 'react-dom';
import { ipcRenderer } from 'electron'; import { ipcRenderer } from 'electron';
import React from 'react';
import { createRoot } from 'react-dom/client';
import { LocalStorageKey } from '@noodl-constants/LocalStorageKey'; import { LocalStorageKey } from '@noodl-constants/LocalStorageKey';
import getDocsEndpoint from '@noodl-utils/getDocsEndpoint'; import getDocsEndpoint from '@noodl-utils/getDocsEndpoint';
import PopupLayer from './views/popuplayer';
import { NewsModal } from './views/NewsModal'; import { NewsModal } from './views/NewsModal';
import PopupLayer from './views/popuplayer';
/** /**
* Display latest whats-new-post if the user hasn't seen one after it was last published * Display latest whats-new-post if the user hasn't seen one after it was last published
@@ -34,12 +35,11 @@ export async function whatsnewRender() {
modalContainer.classList.add('popup-layer-react-modal'); modalContainer.classList.add('popup-layer-react-modal');
PopupLayer.instance.el.find('.popup-layer-modal').before(modalContainer); PopupLayer.instance.el.find('.popup-layer-modal').before(modalContainer);
ReactDOM.render( createRoot(modalContainer).render(
React.createElement(NewsModal, { React.createElement(NewsModal, {
content: latestChangelogPost.content_html, content: latestChangelogPost.content_html,
onFinished: () => ipcRenderer.send('viewer-show') onFinished: () => ipcRenderer.send('viewer-show')
}), })
modalContainer
); );
localStorage.setItem(LocalStorageKey.lastSeenChangelogDate, latestChangelogDate.toString()); localStorage.setItem(LocalStorageKey.lastSeenChangelogDate, latestChangelogDate.toString());