ReactDOM.render -> React.createRoot().render (#4338)

- await for promise that is resolved in ref attribute
of root.render() as an alternative for removed callback
from ReactDOM.render
- await-ing for promise every time when ref needs to be
used after render (e.g. tag selector container), otherwise
ref will be undefined
- additional window.sizeToContent calls to properly size
dialogs with react-rendered content (e.g. create parent),
otherwise the window can cut off some of the content.
This commit is contained in:
Bogdan Abaev 2024-07-05 14:22:06 -07:00 committed by Dan Stillman
parent 4e0c89b352
commit af4bbd2c4d
16 changed files with 125 additions and 107 deletions

View file

@ -39,11 +39,9 @@ var CollectionTree = class CollectionTree extends LibraryTree {
Zotero.debug("Initializing React CollectionTree");
var ref;
opts.domEl = domEl;
let elem = (
<CollectionTree ref={c => ref = c } {...opts} />
);
await new Promise(resolve => ReactDOM.render(elem, domEl, resolve));
await new Promise(resolve => {
ReactDOM.createRoot(domEl).render(<CollectionTree ref={(c) => { ref = c; resolve()} } {...opts } />)
});
Zotero.debug('React CollectionTree initialized');
return ref;
}

View file

@ -65,7 +65,7 @@ function AnnotationBox({ data }) {
Zotero.AnnotationBox = memo(AnnotationBox);
Zotero.AnnotationBox.render = (domEl, props) => {
ReactDOM.render(<AnnotationBox { ...props } />, domEl);
ReactDOM.createRoot(domEl).render(<AnnotationBox { ...props } />);
};
Zotero.AnnotationBox.destroy = (domEl) => {

View file

@ -25,12 +25,17 @@
'use strict';
import React, { memo } from 'react';
import React, { memo, useEffect } from 'react';
import ReactDOM from "react-dom";
import PropTypes from 'prop-types';
import cx from 'classnames';
function CreateParent({ loading, item, toggleAccept }) {
// With React 18, this is required for the window's dialog to be properly sized
useEffect(() => {
window.sizeToContent();
}, []);
// When the input has/does not have characters toggle the accept button on the dialog
const handleInput = (e) => {
if (e.target.value.trim() !== '') {
@ -81,5 +86,5 @@ Zotero.CreateParent.destroy = (domEl) => {
Zotero.CreateParent.render = (domEl, props) => {
ReactDOM.render(<CreateParent { ...props } />, domEl);
ReactDOM.createRoot(domEl).render(<CreateParent { ...props } />);
};

View file

@ -109,6 +109,7 @@ const TabBar = forwardRef(function (props, ref) {
updateOverflowing();
}, 300, { leading: false });
window.addEventListener('resize', handleResize);
props.onLoad();
return () => {
window.removeEventListener('resize', handleResize);
};

View file

@ -857,13 +857,14 @@ Zotero.TagSelector = class TagSelectorContainer extends React.PureComponent {
return this.state.showAutomatic;
}
static init(domEl, opts) {
static async init(domEl, opts) {
var ref;
let elem = (
<TagSelectorContainer ref={c => ref = c } {...opts} />
);
ReactDOM.render(elem, domEl);
ref.domEl = domEl;
await new Promise((resolve) => {
ReactDOM.createRoot(domEl).render(<TagSelectorContainer ref={(c) => {
ref = c;
resolve();
} } {...opts} />);
});
return ref;
}

View file

@ -169,9 +169,8 @@ const Zotero_Import_Wizard = { // eslint-disable-line no-unused-vars
if (this.folder && !showReportErrorButton) {
doneQueueContainer.style.display = 'flex';
ReactDOM.render(
<ProgressQueueTable progressQueue={ Zotero.ProgressQueues.get('recognize') } />,
doneQueue
ReactDOM.createRoot(doneQueue).render(
<ProgressQueueTable progressQueue={ Zotero.ProgressQueues.get('recognize') } />
);
}
else {
@ -381,9 +380,8 @@ const Zotero_Import_Wizard = { // eslint-disable-line no-unused-vars
const progressQueue = document.getElementById('progress-queue');
if (this.folder) {
progressQueueContainer.style.display = 'flex';
ReactDOM.render(
<ProgressQueueTable progressQueue={Zotero.ProgressQueues.get('recognize')} />,
progressQueue
ReactDOM.createRoot(progressQueue).render(
<ProgressQueueTable progressQueue={Zotero.ProgressQueues.get('recognize')} />
);
}
else {

View file

@ -50,10 +50,12 @@ var ItemTree = class ItemTree extends LibraryTree {
Zotero.debug(`Initializing React ItemTree ${opts.id}`);
var ref;
opts.domEl = domEl;
let elem = (
<ItemTree ref={c => ref = c } {...opts} />
);
await new Promise(resolve => ReactDOM.render(elem, domEl, resolve));
await new Promise((resolve) => {
ReactDOM.createRoot(domEl).render(<ItemTree ref={(c) => {
ref = c;
resolve();
} } {...opts} />);
});
Zotero.debug(`React ItemTree ${opts.id} initialized`);
return ref;

View file

@ -39,23 +39,27 @@ const columns = [
function init() {
engines = Zotero.LocateManager.getEngines();
const domEl = document.querySelector('#locateManager-tree');
let elem = (
<VirtualizedTable
getRowCount={() => engines.length}
id="locateManager-table"
ref={ref => tree = ref}
renderItem={VirtualizedTable.makeRowRenderer(getRowData)}
showHeader={true}
multiSelect={true}
columns={columns}
onColumnSort={null}
disableFontSizeScaling={true}
getRowString={index => getRowData(index).name}
onSelectionChange={handleSelectionChange}
onActivate={handleActivate}
/>
);
return new Promise(resolve => ReactDOM.render(elem, domEl, resolve));
return new Promise((resolve) => {
ReactDOM.createRoot(domEl).render(
<VirtualizedTable
getRowCount={() => engines.length}
id="locateManager-table"
ref={(ref) => {
tree = ref;
resolve();
}}
renderItem={VirtualizedTable.makeRowRenderer(getRowData)}
showHeader={true}
multiSelect={true}
columns={columns}
onColumnSort={null}
disableFontSizeScaling={true}
getRowString={index => getRowData(index).name}
onSelectionChange={handleSelectionChange}
onActivate={handleActivate}
/>
);
});
}
function getRowData(index) {

View file

@ -104,25 +104,28 @@ Zotero_Preferences.Cite = {
return false;
}
};
let elem = (
<VirtualizedTable
getRowCount={() => this.styles.length}
id="styleManager-table"
ref={ref => this._tree = ref}
renderItem={makeRowRenderer(index => this.styles[index])}
showHeader={true}
multiSelect={true}
columns={columns}
staticColumns={true}
disableFontSizeScaling={true}
onSelectionChange={selection => document.getElementById('styleManager-delete').disabled = !selection.count}
onKeyDown={handleKeyDown}
getRowString={index => this.styles[index].title}
/>
);
let styleManager = document.getElementById("styleManager");
await new Promise(resolve => ReactDOM.render(elem, styleManager, resolve));
await new Promise((resolve) => {
ReactDOM.createRoot(document.getElementById("styleManager")).render(
<VirtualizedTable
getRowCount={() => this.styles.length}
id="styleManager-table"
ref={(ref) => {
this._tree = ref;
resolve();
}}
renderItem={makeRowRenderer(index => this.styles[index])}
showHeader={true}
multiSelect={true}
columns={columns}
staticColumns={true}
disableFontSizeScaling={true}
onSelectionChange={selection => document.getElementById('styleManager-delete').disabled = !selection.count}
onKeyDown={handleKeyDown}
getRowString={index => this.styles[index].title}
/>
);
});
// Fix style manager showing partially blank until scrolled
setTimeout(() => this._tree.invalidate());

View file

@ -485,23 +485,27 @@ Zotero_Preferences.Export = {
this.updateQuickCopySiteButtons();
};
let elem = (
<VirtualizedTable
getRowCount={() => this._rows.length}
id="quickCopy-siteSettings-table"
ref={ref => this._tree = ref}
renderItem={makeRowRenderer(index => this._rows[index])}
showHeader={true}
columns={columns}
staticColumns={true}
disableFontSizeScaling={true}
onSelectionChange={handleSelectionChange}
onKeyDown={handleKeyDown}
getRowString={index => this._rows[index].domain}
onActivate={(event, indices) => Zotero_Preferences.Export.showQuickCopySiteEditor(true)}
/>
);
await new Promise(resolve => ReactDOM.render(elem, document.getElementById("quickCopy-siteSettings"), resolve));
await new Promise((resolve) => {
ReactDOM.createRoot(document.getElementById("quickCopy-siteSettings")).render(
<VirtualizedTable
getRowCount={() => this._rows.length}
id="quickCopy-siteSettings-table"
ref={(ref) => {
this._tree = ref;
resolve();
}}
renderItem={makeRowRenderer(index => this._rows[index])}
showHeader={true}
columns={columns}
staticColumns={true}
disableFontSizeScaling={true}
onSelectionChange={handleSelectionChange}
onKeyDown={handleKeyDown}
getRowString={index => this._rows[index].domain}
onActivate={(event, indices) => Zotero_Preferences.Export.showQuickCopySiteEditor(true)}
/>
);
});
} else {
this._tree.invalidate();
}

View file

@ -315,22 +315,25 @@ Zotero_Preferences.Sync = {
return false;
}
};
let elem = (
<VirtualizedTable
getRowCount={() => this._rows.length}
id="librariesToSync-table"
ref={ref => this._tree = ref}
renderItem={renderItem}
showHeader={true}
columns={columns}
staticColumns={true}
getRowString={index => this._rows[index].name}
disableFontSizeScaling={true}
onKeyDown={handleKeyDown}
/>
);
ReactDOM.render(elem, document.getElementById("libraries-to-sync-tree"));
await new Promise((resolve) => {
ReactDOM.createRoot(document.getElementById("libraries-to-sync-tree")).render(
<VirtualizedTable
getRowCount={() => this._rows.length}
id="librariesToSync-table"
ref={(ref) => {
this._tree = ref;
resolve();
}}
renderItem={renderItem}
showHeader={true}
columns={columns}
staticColumns={true}
getRowString={index => this._rows[index].name}
disableFontSizeScaling={true}
onKeyDown={handleKeyDown}
/>
);
});
var addRow = function (libraryName, id, checked=false, editable=true) {
this._rows.push({

View file

@ -37,13 +37,11 @@ function _init() {
const domEl = document.querySelector('#tree');
ReactDOM.render(
ReactDOM.createRoot(domEl).render(
<ProgressQueueTable
onActivate={ _handleActivate }
progressQueue={ _progressQueue }
/>,
domEl
);
/>);
}
/**

View file

@ -145,7 +145,7 @@ const Zotero_RTFScan = { // eslint-disable-line no-unused-vars, camelcase
.getElementById('choose-output-file')
.addEventListener('click', this.onChooseOutputFile.bind(this));
ReactDOM.render((
ReactDOM.createRoot(document.getElementById('tree')).render((
<VirtualizedTable
getRowCount={() => this.rows.length}
id="rtfScan-table"
@ -156,7 +156,7 @@ const Zotero_RTFScan = { // eslint-disable-line no-unused-vars, camelcase
containerWidth={document.getElementById('tree').clientWidth}
disableFontSizeScaling={true}
/>
), document.getElementById('tree'));
));
const lastInputFile = Zotero.Prefs.get("rtfScan.lastInputFile");
if (lastInputFile) {

View file

@ -165,7 +165,7 @@ var Zotero_Tabs = new function () {
};
this.init = function () {
ReactDOM.render(
ReactDOM.createRoot(document.getElementById('tab-bar-container')).render(
<TabBar
ref={this._tabBarRef}
onTabSelect={this.select.bind(this)}
@ -173,11 +173,8 @@ var Zotero_Tabs = new function () {
onTabClose={this.close.bind(this)}
onContextMenu={this._openMenu.bind(this)}
refocusReader={this.refocusReader.bind(this)}
/>,
document.getElementById('tab-bar-container'),
() => {
this._update();
}
onLoad={this._update.bind(this)}
/>
);
};

View file

@ -31,7 +31,7 @@ const ReactDOM = require('react-dom');
function init() {
let div = document.querySelector('div');
ReactDOM.render(<DataGeneratorForm/>, div);
ReactDOM.createRoot(div).render(<DataGeneratorForm/>);
}
class DataGeneratorForm extends React.Component {
@ -44,6 +44,10 @@ class DataGeneratorForm extends React.Component {
};
}
componentDidMount() {
window.sizeToContent();
}
render() {
return (
<div>

View file

@ -1688,11 +1688,11 @@ var ZoteroPane = new function()
}
};
this.initTagSelector = function () {
this.initTagSelector = async function () {
try {
var container = document.getElementById('zotero-tag-selector-container');
if (!container.hasAttribute('collapsed') || container.getAttribute('collapsed') == 'false') {
this.tagSelector = Zotero.TagSelector.init(
this.tagSelector = await Zotero.TagSelector.init(
document.getElementById('zotero-tag-selector'),
{
container: 'zotero-tag-selector-container',