From af4bbd2c4d5018b01fe01b1683f99bc71ff1deef Mon Sep 17 00:00:00 2001 From: Bogdan Abaev Date: Fri, 5 Jul 2024 14:22:06 -0700 Subject: [PATCH] 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. --- chrome/content/zotero/collectionTree.jsx | 8 ++-- .../content/zotero/components/annotation.jsx | 2 +- .../components/createParent/createParent.jsx | 9 ++++- chrome/content/zotero/components/tabBar.jsx | 1 + .../containers/tagSelectorContainer.jsx | 13 ++++--- chrome/content/zotero/import/importWizard.js | 10 ++--- chrome/content/zotero/itemTree.jsx | 10 +++-- chrome/content/zotero/locateManager.jsx | 38 ++++++++++-------- .../zotero/preferences/preferences_cite.jsx | 39 ++++++++++--------- .../zotero/preferences/preferences_export.jsx | 38 ++++++++++-------- .../zotero/preferences/preferences_sync.jsx | 35 +++++++++-------- chrome/content/zotero/progressQueueDialog.jsx | 6 +-- chrome/content/zotero/rtfScan.js | 4 +- chrome/content/zotero/tabs.js | 9 ++--- .../content/zotero/tools/data_generator.jsx | 6 ++- chrome/content/zotero/zoteroPane.js | 4 +- 16 files changed, 125 insertions(+), 107 deletions(-) diff --git a/chrome/content/zotero/collectionTree.jsx b/chrome/content/zotero/collectionTree.jsx index f76174b8af..113196a74e 100644 --- a/chrome/content/zotero/collectionTree.jsx +++ b/chrome/content/zotero/collectionTree.jsx @@ -39,11 +39,9 @@ var CollectionTree = class CollectionTree extends LibraryTree { Zotero.debug("Initializing React CollectionTree"); var ref; opts.domEl = domEl; - let elem = ( - ref = c } {...opts} /> - ); - await new Promise(resolve => ReactDOM.render(elem, domEl, resolve)); - + await new Promise(resolve => { + ReactDOM.createRoot(domEl).render( { ref = c; resolve()} } {...opts } />) + }); Zotero.debug('React CollectionTree initialized'); return ref; } diff --git a/chrome/content/zotero/components/annotation.jsx b/chrome/content/zotero/components/annotation.jsx index bb2f9f3d86..865fd8f11d 100644 --- a/chrome/content/zotero/components/annotation.jsx +++ b/chrome/content/zotero/components/annotation.jsx @@ -65,7 +65,7 @@ function AnnotationBox({ data }) { Zotero.AnnotationBox = memo(AnnotationBox); Zotero.AnnotationBox.render = (domEl, props) => { - ReactDOM.render(, domEl); + ReactDOM.createRoot(domEl).render(); }; Zotero.AnnotationBox.destroy = (domEl) => { diff --git a/chrome/content/zotero/components/createParent/createParent.jsx b/chrome/content/zotero/components/createParent/createParent.jsx index fe68219fc3..85e087d9ad 100644 --- a/chrome/content/zotero/components/createParent/createParent.jsx +++ b/chrome/content/zotero/components/createParent/createParent.jsx @@ -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(, domEl); + ReactDOM.createRoot(domEl).render(); }; diff --git a/chrome/content/zotero/components/tabBar.jsx b/chrome/content/zotero/components/tabBar.jsx index 4269b9c5a8..9eb3bdef70 100644 --- a/chrome/content/zotero/components/tabBar.jsx +++ b/chrome/content/zotero/components/tabBar.jsx @@ -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); }; diff --git a/chrome/content/zotero/containers/tagSelectorContainer.jsx b/chrome/content/zotero/containers/tagSelectorContainer.jsx index c3125ee725..c8b29065f6 100644 --- a/chrome/content/zotero/containers/tagSelectorContainer.jsx +++ b/chrome/content/zotero/containers/tagSelectorContainer.jsx @@ -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 = ( - ref = c } {...opts} /> - ); - ReactDOM.render(elem, domEl); - ref.domEl = domEl; + await new Promise((resolve) => { + ReactDOM.createRoot(domEl).render( { + ref = c; + resolve(); + } } {...opts} />); + }); return ref; } diff --git a/chrome/content/zotero/import/importWizard.js b/chrome/content/zotero/import/importWizard.js index 5720baf02f..8151188759 100644 --- a/chrome/content/zotero/import/importWizard.js +++ b/chrome/content/zotero/import/importWizard.js @@ -169,9 +169,8 @@ const Zotero_Import_Wizard = { // eslint-disable-line no-unused-vars if (this.folder && !showReportErrorButton) { doneQueueContainer.style.display = 'flex'; - ReactDOM.render( - , - doneQueue + ReactDOM.createRoot(doneQueue).render( + ); } 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( - , - progressQueue + ReactDOM.createRoot(progressQueue).render( + ); } else { diff --git a/chrome/content/zotero/itemTree.jsx b/chrome/content/zotero/itemTree.jsx index cabe996b5c..3503bac4a0 100644 --- a/chrome/content/zotero/itemTree.jsx +++ b/chrome/content/zotero/itemTree.jsx @@ -50,10 +50,12 @@ var ItemTree = class ItemTree extends LibraryTree { Zotero.debug(`Initializing React ItemTree ${opts.id}`); var ref; opts.domEl = domEl; - let elem = ( - ref = c } {...opts} /> - ); - await new Promise(resolve => ReactDOM.render(elem, domEl, resolve)); + await new Promise((resolve) => { + ReactDOM.createRoot(domEl).render( { + ref = c; + resolve(); + } } {...opts} />); + }); Zotero.debug(`React ItemTree ${opts.id} initialized`); return ref; diff --git a/chrome/content/zotero/locateManager.jsx b/chrome/content/zotero/locateManager.jsx index 03f5cf6e43..0884cae469 100644 --- a/chrome/content/zotero/locateManager.jsx +++ b/chrome/content/zotero/locateManager.jsx @@ -39,23 +39,27 @@ const columns = [ function init() { engines = Zotero.LocateManager.getEngines(); const domEl = document.querySelector('#locateManager-tree'); - let elem = ( - 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( + 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) { diff --git a/chrome/content/zotero/preferences/preferences_cite.jsx b/chrome/content/zotero/preferences/preferences_cite.jsx index c85cebf5f1..9379045a43 100644 --- a/chrome/content/zotero/preferences/preferences_cite.jsx +++ b/chrome/content/zotero/preferences/preferences_cite.jsx @@ -104,25 +104,28 @@ Zotero_Preferences.Cite = { return false; } }; - let elem = ( - 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( + 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()); diff --git a/chrome/content/zotero/preferences/preferences_export.jsx b/chrome/content/zotero/preferences/preferences_export.jsx index 6b193c776c..21c743109c 100644 --- a/chrome/content/zotero/preferences/preferences_export.jsx +++ b/chrome/content/zotero/preferences/preferences_export.jsx @@ -485,23 +485,27 @@ Zotero_Preferences.Export = { this.updateQuickCopySiteButtons(); }; - let elem = ( - 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( + 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(); } diff --git a/chrome/content/zotero/preferences/preferences_sync.jsx b/chrome/content/zotero/preferences/preferences_sync.jsx index 4516aab9e3..1cc4d25520 100644 --- a/chrome/content/zotero/preferences/preferences_sync.jsx +++ b/chrome/content/zotero/preferences/preferences_sync.jsx @@ -315,22 +315,25 @@ Zotero_Preferences.Sync = { return false; } }; - let elem = ( - 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( + 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({ diff --git a/chrome/content/zotero/progressQueueDialog.jsx b/chrome/content/zotero/progressQueueDialog.jsx index 0e42fd23d8..3cb410f4d2 100644 --- a/chrome/content/zotero/progressQueueDialog.jsx +++ b/chrome/content/zotero/progressQueueDialog.jsx @@ -37,13 +37,11 @@ function _init() { const domEl = document.querySelector('#tree'); - ReactDOM.render( + ReactDOM.createRoot(domEl).render( , - domEl - ); + />); } /** diff --git a/chrome/content/zotero/rtfScan.js b/chrome/content/zotero/rtfScan.js index 571a2f4478..0f8623dc33 100644 --- a/chrome/content/zotero/rtfScan.js +++ b/chrome/content/zotero/rtfScan.js @@ -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(( 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) { diff --git a/chrome/content/zotero/tabs.js b/chrome/content/zotero/tabs.js index 34b3f5deb0..c127dd9e3f 100644 --- a/chrome/content/zotero/tabs.js +++ b/chrome/content/zotero/tabs.js @@ -165,7 +165,7 @@ var Zotero_Tabs = new function () { }; this.init = function () { - ReactDOM.render( + ReactDOM.createRoot(document.getElementById('tab-bar-container')).render( , - document.getElementById('tab-bar-container'), - () => { - this._update(); - } + onLoad={this._update.bind(this)} + /> ); }; diff --git a/chrome/content/zotero/tools/data_generator.jsx b/chrome/content/zotero/tools/data_generator.jsx index f791ce60ee..142fd4ab2f 100644 --- a/chrome/content/zotero/tools/data_generator.jsx +++ b/chrome/content/zotero/tools/data_generator.jsx @@ -31,7 +31,7 @@ const ReactDOM = require('react-dom'); function init() { let div = document.querySelector('div'); - ReactDOM.render(, div); + ReactDOM.createRoot(div).render(); } class DataGeneratorForm extends React.Component { @@ -44,6 +44,10 @@ class DataGeneratorForm extends React.Component { }; } + componentDidMount() { + window.sizeToContent(); + } + render() { return (
diff --git a/chrome/content/zotero/zoteroPane.js b/chrome/content/zotero/zoteroPane.js index d0832eca7d..53b451cc8b 100644 --- a/chrome/content/zotero/zoteroPane.js +++ b/chrome/content/zotero/zoteroPane.js @@ -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',