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"); Zotero.debug("Initializing React CollectionTree");
var ref; var ref;
opts.domEl = domEl; opts.domEl = domEl;
let elem = ( await new Promise(resolve => {
<CollectionTree ref={c => ref = c } {...opts} /> ReactDOM.createRoot(domEl).render(<CollectionTree ref={(c) => { ref = c; resolve()} } {...opts } />)
); });
await new Promise(resolve => ReactDOM.render(elem, domEl, resolve));
Zotero.debug('React CollectionTree initialized'); Zotero.debug('React CollectionTree initialized');
return ref; return ref;
} }

View file

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

View file

@ -25,12 +25,17 @@
'use strict'; 'use strict';
import React, { memo } from 'react'; import React, { memo, useEffect } from 'react';
import ReactDOM from "react-dom"; import ReactDOM from "react-dom";
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import cx from 'classnames'; import cx from 'classnames';
function CreateParent({ loading, item, toggleAccept }) { 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 // When the input has/does not have characters toggle the accept button on the dialog
const handleInput = (e) => { const handleInput = (e) => {
if (e.target.value.trim() !== '') { if (e.target.value.trim() !== '') {
@ -81,5 +86,5 @@ Zotero.CreateParent.destroy = (domEl) => {
Zotero.CreateParent.render = (domEl, props) => { 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(); updateOverflowing();
}, 300, { leading: false }); }, 300, { leading: false });
window.addEventListener('resize', handleResize); window.addEventListener('resize', handleResize);
props.onLoad();
return () => { return () => {
window.removeEventListener('resize', handleResize); window.removeEventListener('resize', handleResize);
}; };

View file

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

View file

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

View file

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

View file

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

View file

@ -104,25 +104,28 @@ Zotero_Preferences.Cite = {
return false; 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) => {
await new Promise(resolve => ReactDOM.render(elem, styleManager, 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 // Fix style manager showing partially blank until scrolled
setTimeout(() => this._tree.invalidate()); setTimeout(() => this._tree.invalidate());

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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