Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Element: enable concurrent mode by implementing mount/unmount with createRoot #46467

Merged
merged 12 commits into from
Feb 1, 2023

Conversation

jsnajdr
Copy link
Member

@jsnajdr jsnajdr commented Dec 12, 2022

Reimplement the render, hydrate and unmountComponentAtNode helpers in @wordpress/element using the new createRoot and hydrateRoot APIs from React 18, instead of using the legacy React 17 implementations.

The side effect of this is switching the entire production editor to concurrent mode. Let's see if this breaks the editor or not.

In his original React 18 upgrade PR, @youknowriad did many changes to various components (summarized here) which were not included in the React 18 upgrade that got eventually merged. Maybe we'll need to merge these changes, too, after all, because otherwise concurrent mode will break? Let's test this hypothesis 🙂

@jsnajdr jsnajdr added the [Package] Element /packages/element label Dec 12, 2022
@jsnajdr jsnajdr requested review from youknowriad and tyxla December 12, 2022 14:26
@jsnajdr jsnajdr self-assigned this Dec 12, 2022
@jsnajdr jsnajdr requested a review from ajitbohra as a code owner December 12, 2022 14:26
@github-actions
Copy link

github-actions bot commented Dec 12, 2022

Size Change: -173 B (0%)

Total Size: 1.31 MB

Filename Size Change
build/block-editor/index.min.js 192 kB +24 B (0%)
build/block-library/index.min.js 199 kB -4 B (0%)
build/components/index.min.js 203 kB +6 B (0%)
build/compose/index.min.js 12.3 kB +2 B (0%)
build/edit-post/index.min.js 34.5 kB -47 B (0%)
build/edit-site/index.min.js 63 kB -118 B (0%)
build/edit-widgets/index.min.js 16.9 kB -12 B (0%)
build/editor/index.min.js 45 kB -24 B (0%)
ℹ️ View Unchanged
Filename Size
build/a11y/index.min.js 993 B
build/annotations/index.min.js 2.78 kB
build/api-fetch/index.min.js 2.27 kB
build/autop/index.min.js 2.15 kB
build/blob/index.min.js 483 B
build/block-directory/index.min.js 7.16 kB
build/block-directory/style-rtl.css 1.04 kB
build/block-directory/style.css 1.04 kB
build/block-editor/content-rtl.css 3.62 kB
build/block-editor/content.css 3.62 kB
build/block-editor/default-editor-styles-rtl.css 403 B
build/block-editor/default-editor-styles.css 403 B
build/block-editor/style-rtl.css 14.3 kB
build/block-editor/style.css 14.3 kB
build/block-library/blocks/archives/editor-rtl.css 61 B
build/block-library/blocks/archives/editor.css 60 B
build/block-library/blocks/archives/style-rtl.css 90 B
build/block-library/blocks/archives/style.css 90 B
build/block-library/blocks/audio/editor-rtl.css 150 B
build/block-library/blocks/audio/editor.css 150 B
build/block-library/blocks/audio/style-rtl.css 122 B
build/block-library/blocks/audio/style.css 122 B
build/block-library/blocks/audio/theme-rtl.css 138 B
build/block-library/blocks/audio/theme.css 138 B
build/block-library/blocks/avatar/editor-rtl.css 116 B
build/block-library/blocks/avatar/editor.css 116 B
build/block-library/blocks/avatar/style-rtl.css 84 B
build/block-library/blocks/avatar/style.css 84 B
build/block-library/blocks/block/editor-rtl.css 305 B
build/block-library/blocks/block/editor.css 305 B
build/block-library/blocks/button/editor-rtl.css 485 B
build/block-library/blocks/button/editor.css 485 B
build/block-library/blocks/button/style-rtl.css 532 B
build/block-library/blocks/button/style.css 532 B
build/block-library/blocks/buttons/editor-rtl.css 337 B
build/block-library/blocks/buttons/editor.css 337 B
build/block-library/blocks/buttons/style-rtl.css 332 B
build/block-library/blocks/buttons/style.css 332 B
build/block-library/blocks/calendar/style-rtl.css 239 B
build/block-library/blocks/calendar/style.css 239 B
build/block-library/blocks/categories/editor-rtl.css 84 B
build/block-library/blocks/categories/editor.css 83 B
build/block-library/blocks/categories/style-rtl.css 100 B
build/block-library/blocks/categories/style.css 100 B
build/block-library/blocks/code/editor-rtl.css 53 B
build/block-library/blocks/code/editor.css 53 B
build/block-library/blocks/code/style-rtl.css 121 B
build/block-library/blocks/code/style.css 121 B
build/block-library/blocks/code/theme-rtl.css 124 B
build/block-library/blocks/code/theme.css 124 B
build/block-library/blocks/columns/editor-rtl.css 108 B
build/block-library/blocks/columns/editor.css 108 B
build/block-library/blocks/columns/style-rtl.css 406 B
build/block-library/blocks/columns/style.css 406 B
build/block-library/blocks/comment-author-avatar/editor-rtl.css 125 B
build/block-library/blocks/comment-author-avatar/editor.css 125 B
build/block-library/blocks/comment-content/style-rtl.css 92 B
build/block-library/blocks/comment-content/style.css 92 B
build/block-library/blocks/comment-template/style-rtl.css 199 B
build/block-library/blocks/comment-template/style.css 198 B
build/block-library/blocks/comments-pagination-numbers/editor-rtl.css 123 B
build/block-library/blocks/comments-pagination-numbers/editor.css 121 B
build/block-library/blocks/comments-pagination/editor-rtl.css 222 B
build/block-library/blocks/comments-pagination/editor.css 209 B
build/block-library/blocks/comments-pagination/style-rtl.css 235 B
build/block-library/blocks/comments-pagination/style.css 231 B
build/block-library/blocks/comments-title/editor-rtl.css 75 B
build/block-library/blocks/comments-title/editor.css 75 B
build/block-library/blocks/comments/editor-rtl.css 840 B
build/block-library/blocks/comments/editor.css 839 B
build/block-library/blocks/comments/style-rtl.css 637 B
build/block-library/blocks/comments/style.css 636 B
build/block-library/blocks/cover/editor-rtl.css 612 B
build/block-library/blocks/cover/editor.css 613 B
build/block-library/blocks/cover/style-rtl.css 1.57 kB
build/block-library/blocks/cover/style.css 1.56 kB
build/block-library/blocks/embed/editor-rtl.css 293 B
build/block-library/blocks/embed/editor.css 293 B
build/block-library/blocks/embed/style-rtl.css 410 B
build/block-library/blocks/embed/style.css 410 B
build/block-library/blocks/embed/theme-rtl.css 138 B
build/block-library/blocks/embed/theme.css 138 B
build/block-library/blocks/file/editor-rtl.css 300 B
build/block-library/blocks/file/editor.css 300 B
build/block-library/blocks/file/style-rtl.css 253 B
build/block-library/blocks/file/style.css 254 B
build/block-library/blocks/file/view.min.js 353 B
build/block-library/blocks/freeform/editor-rtl.css 2.44 kB
build/block-library/blocks/freeform/editor.css 2.44 kB
build/block-library/blocks/gallery/editor-rtl.css 984 B
build/block-library/blocks/gallery/editor.css 988 B
build/block-library/blocks/gallery/style-rtl.css 1.55 kB
build/block-library/blocks/gallery/style.css 1.55 kB
build/block-library/blocks/gallery/theme-rtl.css 122 B
build/block-library/blocks/gallery/theme.css 122 B
build/block-library/blocks/group/editor-rtl.css 654 B
build/block-library/blocks/group/editor.css 654 B
build/block-library/blocks/group/style-rtl.css 57 B
build/block-library/blocks/group/style.css 57 B
build/block-library/blocks/group/theme-rtl.css 78 B
build/block-library/blocks/group/theme.css 78 B
build/block-library/blocks/heading/style-rtl.css 76 B
build/block-library/blocks/heading/style.css 76 B
build/block-library/blocks/html/editor-rtl.css 332 B
build/block-library/blocks/html/editor.css 333 B
build/block-library/blocks/image/editor-rtl.css 829 B
build/block-library/blocks/image/editor.css 828 B
build/block-library/blocks/image/style-rtl.css 627 B
build/block-library/blocks/image/style.css 630 B
build/block-library/blocks/image/theme-rtl.css 137 B
build/block-library/blocks/image/theme.css 137 B
build/block-library/blocks/latest-comments/style-rtl.css 298 B
build/block-library/blocks/latest-comments/style.css 298 B
build/block-library/blocks/latest-posts/editor-rtl.css 213 B
build/block-library/blocks/latest-posts/editor.css 212 B
build/block-library/blocks/latest-posts/style-rtl.css 478 B
build/block-library/blocks/latest-posts/style.css 478 B
build/block-library/blocks/list/style-rtl.css 88 B
build/block-library/blocks/list/style.css 88 B
build/block-library/blocks/media-text/editor-rtl.css 266 B
build/block-library/blocks/media-text/editor.css 263 B
build/block-library/blocks/media-text/style-rtl.css 507 B
build/block-library/blocks/media-text/style.css 505 B
build/block-library/blocks/more/editor-rtl.css 431 B
build/block-library/blocks/more/editor.css 431 B
build/block-library/blocks/navigation-link/editor-rtl.css 716 B
build/block-library/blocks/navigation-link/editor.css 715 B
build/block-library/blocks/navigation-link/style-rtl.css 115 B
build/block-library/blocks/navigation-link/style.css 115 B
build/block-library/blocks/navigation-submenu/editor-rtl.css 299 B
build/block-library/blocks/navigation-submenu/editor.css 299 B
build/block-library/blocks/navigation/editor-rtl.css 2.13 kB
build/block-library/blocks/navigation/editor.css 2.14 kB
build/block-library/blocks/navigation/style-rtl.css 2.22 kB
build/block-library/blocks/navigation/style.css 2.2 kB
build/block-library/blocks/navigation/view-modal.min.js 2.81 kB
build/block-library/blocks/navigation/view.min.js 447 B
build/block-library/blocks/nextpage/editor-rtl.css 395 B
build/block-library/blocks/nextpage/editor.css 395 B
build/block-library/blocks/page-list/editor-rtl.css 376 B
build/block-library/blocks/page-list/editor.css 376 B
build/block-library/blocks/page-list/style-rtl.css 175 B
build/block-library/blocks/page-list/style.css 175 B
build/block-library/blocks/paragraph/editor-rtl.css 174 B
build/block-library/blocks/paragraph/editor.css 174 B
build/block-library/blocks/paragraph/style-rtl.css 279 B
build/block-library/blocks/paragraph/style.css 281 B
build/block-library/blocks/post-author/style-rtl.css 175 B
build/block-library/blocks/post-author/style.css 176 B
build/block-library/blocks/post-comments-form/editor-rtl.css 96 B
build/block-library/blocks/post-comments-form/editor.css 96 B
build/block-library/blocks/post-comments-form/style-rtl.css 501 B
build/block-library/blocks/post-comments-form/style.css 501 B
build/block-library/blocks/post-date/style-rtl.css 61 B
build/block-library/blocks/post-date/style.css 61 B
build/block-library/blocks/post-excerpt/editor-rtl.css 73 B
build/block-library/blocks/post-excerpt/editor.css 73 B
build/block-library/blocks/post-excerpt/style-rtl.css 69 B
build/block-library/blocks/post-excerpt/style.css 69 B
build/block-library/blocks/post-featured-image/editor-rtl.css 586 B
build/block-library/blocks/post-featured-image/editor.css 584 B
build/block-library/blocks/post-featured-image/style-rtl.css 318 B
build/block-library/blocks/post-featured-image/style.css 318 B
build/block-library/blocks/post-navigation-link/style-rtl.css 153 B
build/block-library/blocks/post-navigation-link/style.css 153 B
build/block-library/blocks/post-template/editor-rtl.css 99 B
build/block-library/blocks/post-template/editor.css 98 B
build/block-library/blocks/post-template/style-rtl.css 282 B
build/block-library/blocks/post-template/style.css 282 B
build/block-library/blocks/post-terms/style-rtl.css 96 B
build/block-library/blocks/post-terms/style.css 96 B
build/block-library/blocks/post-title/style-rtl.css 100 B
build/block-library/blocks/post-title/style.css 100 B
build/block-library/blocks/preformatted/style-rtl.css 103 B
build/block-library/blocks/preformatted/style.css 103 B
build/block-library/blocks/pullquote/editor-rtl.css 135 B
build/block-library/blocks/pullquote/editor.css 135 B
build/block-library/blocks/pullquote/style-rtl.css 326 B
build/block-library/blocks/pullquote/style.css 325 B
build/block-library/blocks/pullquote/theme-rtl.css 167 B
build/block-library/blocks/pullquote/theme.css 167 B
build/block-library/blocks/query-pagination-numbers/editor-rtl.css 122 B
build/block-library/blocks/query-pagination-numbers/editor.css 121 B
build/block-library/blocks/query-pagination/editor-rtl.css 221 B
build/block-library/blocks/query-pagination/editor.css 211 B
build/block-library/blocks/query-pagination/style-rtl.css 288 B
build/block-library/blocks/query-pagination/style.css 284 B
build/block-library/blocks/query-title/style-rtl.css 63 B
build/block-library/blocks/query-title/style.css 63 B
build/block-library/blocks/query/editor-rtl.css 440 B
build/block-library/blocks/query/editor.css 440 B
build/block-library/blocks/quote/style-rtl.css 213 B
build/block-library/blocks/quote/style.css 213 B
build/block-library/blocks/quote/theme-rtl.css 223 B
build/block-library/blocks/quote/theme.css 226 B
build/block-library/blocks/read-more/style-rtl.css 132 B
build/block-library/blocks/read-more/style.css 132 B
build/block-library/blocks/rss/editor-rtl.css 149 B
build/block-library/blocks/rss/editor.css 149 B
build/block-library/blocks/rss/style-rtl.css 289 B
build/block-library/blocks/rss/style.css 288 B
build/block-library/blocks/search/editor-rtl.css 165 B
build/block-library/blocks/search/editor.css 165 B
build/block-library/blocks/search/style-rtl.css 409 B
build/block-library/blocks/search/style.css 406 B
build/block-library/blocks/search/theme-rtl.css 114 B
build/block-library/blocks/search/theme.css 114 B
build/block-library/blocks/separator/editor-rtl.css 146 B
build/block-library/blocks/separator/editor.css 146 B
build/block-library/blocks/separator/style-rtl.css 234 B
build/block-library/blocks/separator/style.css 234 B
build/block-library/blocks/separator/theme-rtl.css 194 B
build/block-library/blocks/separator/theme.css 194 B
build/block-library/blocks/shortcode/editor-rtl.css 474 B
build/block-library/blocks/shortcode/editor.css 474 B
build/block-library/blocks/site-logo/editor-rtl.css 490 B
build/block-library/blocks/site-logo/editor.css 490 B
build/block-library/blocks/site-logo/style-rtl.css 203 B
build/block-library/blocks/site-logo/style.css 203 B
build/block-library/blocks/site-tagline/editor-rtl.css 86 B
build/block-library/blocks/site-tagline/editor.css 86 B
build/block-library/blocks/site-title/editor-rtl.css 116 B
build/block-library/blocks/site-title/editor.css 116 B
build/block-library/blocks/site-title/style-rtl.css 57 B
build/block-library/blocks/site-title/style.css 57 B
build/block-library/blocks/social-link/editor-rtl.css 184 B
build/block-library/blocks/social-link/editor.css 184 B
build/block-library/blocks/social-links/editor-rtl.css 674 B
build/block-library/blocks/social-links/editor.css 673 B
build/block-library/blocks/social-links/style-rtl.css 1.4 kB
build/block-library/blocks/social-links/style.css 1.39 kB
build/block-library/blocks/spacer/editor-rtl.css 332 B
build/block-library/blocks/spacer/editor.css 332 B
build/block-library/blocks/spacer/style-rtl.css 48 B
build/block-library/blocks/spacer/style.css 48 B
build/block-library/blocks/table/editor-rtl.css 433 B
build/block-library/blocks/table/editor.css 433 B
build/block-library/blocks/table/style-rtl.css 651 B
build/block-library/blocks/table/style.css 650 B
build/block-library/blocks/table/theme-rtl.css 157 B
build/block-library/blocks/table/theme.css 157 B
build/block-library/blocks/tag-cloud/style-rtl.css 251 B
build/block-library/blocks/tag-cloud/style.css 253 B
build/block-library/blocks/template-part/editor-rtl.css 404 B
build/block-library/blocks/template-part/editor.css 404 B
build/block-library/blocks/template-part/theme-rtl.css 101 B
build/block-library/blocks/template-part/theme.css 101 B
build/block-library/blocks/text-columns/editor-rtl.css 95 B
build/block-library/blocks/text-columns/editor.css 95 B
build/block-library/blocks/text-columns/style-rtl.css 166 B
build/block-library/blocks/text-columns/style.css 166 B
build/block-library/blocks/verse/style-rtl.css 99 B
build/block-library/blocks/verse/style.css 99 B
build/block-library/blocks/video/editor-rtl.css 552 B
build/block-library/blocks/video/editor.css 555 B
build/block-library/blocks/video/style-rtl.css 179 B
build/block-library/blocks/video/style.css 179 B
build/block-library/blocks/video/theme-rtl.css 139 B
build/block-library/blocks/video/theme.css 139 B
build/block-library/classic-rtl.css 162 B
build/block-library/classic.css 162 B
build/block-library/common-rtl.css 1.05 kB
build/block-library/common.css 1.05 kB
build/block-library/editor-elements-rtl.css 75 B
build/block-library/editor-elements.css 75 B
build/block-library/editor-rtl.css 11.5 kB
build/block-library/editor.css 11.5 kB
build/block-library/elements-rtl.css 54 B
build/block-library/elements.css 54 B
build/block-library/reset-rtl.css 478 B
build/block-library/reset.css 478 B
build/block-library/style-rtl.css 12.4 kB
build/block-library/style.css 12.4 kB
build/block-library/theme-rtl.css 698 B
build/block-library/theme.css 703 B
build/block-serialization-default-parser/index.min.js 1.13 kB
build/block-serialization-spec-parser/index.min.js 2.83 kB
build/blocks/index.min.js 50.4 kB
build/components/style-rtl.css 11.6 kB
build/components/style.css 11.7 kB
build/core-data/index.min.js 15.9 kB
build/customize-widgets/index.min.js 11.8 kB
build/customize-widgets/style-rtl.css 1.41 kB
build/customize-widgets/style.css 1.41 kB
build/data-controls/index.min.js 663 B
build/data/index.min.js 8.06 kB
build/date/index.min.js 32.1 kB
build/deprecated/index.min.js 518 B
build/dom-ready/index.min.js 336 B
build/dom/index.min.js 4.71 kB
build/edit-post/classic-rtl.css 571 B
build/edit-post/classic.css 571 B
build/edit-post/style-rtl.css 7.46 kB
build/edit-post/style.css 7.45 kB
build/edit-site/style-rtl.css 9.44 kB
build/edit-site/style.css 9.44 kB
build/edit-widgets/style-rtl.css 4.49 kB
build/edit-widgets/style.css 4.49 kB
build/editor/style-rtl.css 3.68 kB
build/editor/style.css 3.67 kB
build/element/index.min.js 4.93 kB
build/escape-html/index.min.js 548 B
build/experiments/index.min.js 905 B
build/format-library/index.min.js 7.23 kB
build/format-library/style-rtl.css 598 B
build/format-library/style.css 597 B
build/hooks/index.min.js 1.66 kB
build/html-entities/index.min.js 454 B
build/i18n/index.min.js 3.79 kB
build/is-shallow-equal/index.min.js 535 B
build/keyboard-shortcuts/index.min.js 1.79 kB
build/keycodes/index.min.js 1.88 kB
build/list-reusable-blocks/index.min.js 2.14 kB
build/list-reusable-blocks/style-rtl.css 865 B
build/list-reusable-blocks/style.css 865 B
build/media-utils/index.min.js 2.99 kB
build/notices/index.min.js 977 B
build/plugins/index.min.js 1.95 kB
build/preferences-persistence/index.min.js 2.23 kB
build/preferences/index.min.js 1.35 kB
build/primitives/index.min.js 960 B
build/priority-queue/index.min.js 1.52 kB
build/react-i18n/index.min.js 702 B
build/react-refresh-entry/index.min.js 8.44 kB
build/react-refresh-runtime/index.min.js 7.31 kB
build/redux-routine/index.min.js 2.75 kB
build/reusable-blocks/index.min.js 2.26 kB
build/reusable-blocks/style-rtl.css 265 B
build/reusable-blocks/style.css 265 B
build/rich-text/index.min.js 10.8 kB
build/server-side-render/index.min.js 2.09 kB
build/shortcode/index.min.js 1.52 kB
build/style-engine/index.min.js 1.53 kB
build/token-list/index.min.js 650 B
build/url/index.min.js 3.69 kB
build/vendors/inert-polyfill.min.js 2.48 kB
build/vendors/react-dom.min.js 41.8 kB
build/vendors/react.min.js 4.02 kB
build/viewport/index.min.js 1.09 kB
build/warning/index.min.js 280 B
build/widgets/index.min.js 7.31 kB
build/widgets/style-rtl.css 1.18 kB
build/widgets/style.css 1.18 kB
build/wordcount/index.min.js 1.06 kB

compressed-size-action

Copy link
Member

@tyxla tyxla left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you for working on this @jsnajdr! It's exciting to see!

Given the exposure of these packages, I wonder if we should reconsider legacy support though. I believe we can't just drop it. See my other comment.

* @param {import('./react').WPElement} element Element to render.
* @param {HTMLElement} target DOM node into which element should be rendered.
*/
export function render( element, target ) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As much as I'd like to just switch to the React 18 APIs and call it "done", I think we should play more responsibly towards the community here and keep exposing the legacy React 17 APIs. They might be necessary for consumers of the @wordpress packages that will upgrade to the React 18 dependency but will need additional time and effort to switch to the concurrent rendering mode.

My personal opinion is that we could, and should be exposing those legacy APIs indefinitely, or at least until we upgrade to a React version that stops supporting them or completely drops them.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The main reason why I decided to reimplement render in terms of createRoot, instead of exporting createRoot from @wordpress/element, and use createRoot to mount the editor, is that createRoot is not very compatible with how we are currently mounting the editors. Let me explain.

Our editor packages, edit-post, edit-site, edit-widgets, export two functions like:

function initializeEditor( target ) {
  render( <Editor />, target );
}

function reinitializeEditor( target ) {
  unmountComponentAtNode( target );
  render( <Editor />, target );
}

This is easy to implement with the legacy render API, but what about createRoot? I would need to keep a Map from targets to roots, and then the two function would need to do lookups in that Map to convert the target argument to the root that corresponds to it.

Not a big deal, currently the render implementation in @wordpress/element does that internally, too, but for the initial version of the PR I decided to avoid this.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That makes sense.

I think we can still keep your implementation, I actually like your idea about using a WeakMap here - however, my main concern is about backward compatibility and continuing to expose the old APIs the same way we were exposing them before.

So perhaps we can just continue exposing the legacy APIs, and keep your implementation, but rename it to something else? We could and probably should expose the raw React 18 APIs as well, just in case someone wants to use them.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think reinitializeEditor is in general called inside initialize meaning we can easily pass the root. That said, even that behavior of "retrying" when errors happens is something I think we should reconsider, I never used it and I believe it's just useless complication

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

reinitializeEditor is a public API though. There's a wp.editPost.reinitializeEditor function exposed that anyone can call.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

mmm right, looking at the results here https://wpdirectory.net/search/01GM5BGZXFN1H5G6T568MC7C49 It seems no one is actually using it, so we could consider removing it (dev note). If we want to be extra careful we can use the weak map technique

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We could also deprecate it if we want to gracefully remove it.

@jsnajdr
Copy link
Member Author

jsnajdr commented Dec 13, 2022

There is trouble with mobile (RN) unit tests. The RN environment uses a minimalistic DOM implementation, jsdom-jscore-rn, and sets the window and document globals to instances of this DOM. But the minimalistic DOM elements from this library don't support, among others, an element.style attribute. But the react-dom package tries to read it during static initialization:

https://github.com/facebook/react/blob/v18.2.0/packages/react-dom/src/events/getVendorPrefixedEventName.js#L51

And then crashes when trying to use it as object.

react-dom stuff used to only be reexported from @wordpress/element and if no export was used, react-dom package wasn't executed. But now we use react-dom to implement our wrappers. And then the tests fails.

One solution would be to reexport the original render, and createRoot functions and don't implement any wrappers in @wordpress/element. This solution was already discussed and now we have a new argument in its favor 🙂

I think it will be also worth reporting a React bug -- take into account that weird libraries like jsdom-jscore-rn exist and work with the DOM more carefully.

@tyxla
Copy link
Member

tyxla commented Dec 13, 2022

One solution would be to reexport the original render, and createRoot functions and don't implement any wrappers in @wordpress/element. This solution was already discussed and now we have a new argument in its favor 🙂

I definitely don't object to that 😉 Do you envision any downsides if we go with that approach?

@jsnajdr
Copy link
Member Author

jsnajdr commented Dec 13, 2022

Do you envision any downsides if we go with that approach?

I see only upsides 🙂 Starting with the premise that @wordpress/element was a mistake and shouldn't really exist, it follows that if it has to exist, it should be a 1:1 re-export of react and react-dom. That means re-exporting the native render and createRoot, not reimplementing render ourselves.

@@ -185,15 +134,13 @@ export function initializeEditor(
window.addEventListener( 'dragover', ( e ) => e.preventDefault(), false );
window.addEventListener( 'drop', ( e ) => e.preventDefault(), false );

render(
createRoot( target ).render(
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Might be worth returning the root in initializeEditor as well. So that we can allow users to unmount the tree as unmountComponentAtNode is replaced.

Copy link
Member

@tyxla tyxla left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've given this a fair amount of testing and noticed that many features don't work at all and simply are static. I can't toggle sections and switch between tabs, unless I trigger an external re-render (@jsnajdr was demoing similar issues in a call yesterday).

I've noticed that when I disable StrictMode, it starts working again. cc @youknowriad who enabled it back in #7202.

So I've pushed a commit that removes strict mode for the time being.

If we end up with this, we might want to audit the repo for obsolete / legacy practices and refactor away from those as soon as possible.

@jsnajdr
Copy link
Member Author

jsnajdr commented Dec 15, 2022

So I've pushed a commit that removes strict mode for the time being.

Did you try to figure out why exactly these features (section toggling, tab switching) don't work? StrictMode, in development mode, does things like running effects twice etc. Does Gutenberg break because of that? Or maybe disabling StrictMode disables the concurrent mode? In that case this PR would end up being a bit pointless, except maybe removing a React warning.

@tyxla
Copy link
Member

tyxla commented Dec 15, 2022

Did you try to figure out why exactly these features (section toggling, tab switching) don't work? StrictMode, in development mode, does things like running effects twice etc. Does Gutenberg break because of that?

Yes, I'm seeing double rendering which also triggers the double running of effects, which seems to be unexpected for Gutenberg. In many areas our toggling mechanisms work like "enable if it's disabled, disable if it's enabled", and not "enable regardless of whether it's disabled, disable regardless of whether it's disabled". This should be working out of the box, but it doesn't when things run twice. It just makes it very difficult to locate a problem and start diagnosing it because with strict mode enabled there are just too many things that aren't working well. Are you suggesting that we keep it enabled and start fixing them one by one?

Or maybe disabling StrictMode disables the concurrent mode?

I don't think it does. It will produce a bunch of warnings when some of the legacy APIs or practices are used, but I don't see anything that would specifically disable concurrent features.

In that case this PR would end up being a bit pointless, except maybe removing a React warning.

I'm not sure I understand what you mean - could you elaborate?

@jsnajdr
Copy link
Member Author

jsnajdr commented Dec 15, 2022

I'm not sure I understand what you mean - could you elaborate?

I mean, if absence of StrictMode disabled the concurrent mode, then the PR wouldn't enable it 🙂 But it seems it doesn't disable concurrent mode, it's just the double effect that break things.

@jsnajdr
Copy link
Member Author

jsnajdr commented Dec 19, 2022

Yes, I'm seeing double rendering which also triggers the double running of effects, which seems to be unexpected for Gutenberg.

It would be a nice project to fix all these double effects, and update the affected components to be concurrent-mode compatible.

By the way @youknowriad already updated many components and hooks in #32765, the first version of React 18 migration, closed without merging, they are summarized in this comment: #32765 (comment)

I'm wondering, were any of the components that you see double-effecting now, were they already modified in #32765? It still bugs me that in that PR Riad did so many updates that we didn't have to do in #45235 at all.

@jsnajdr jsnajdr force-pushed the enable/react-18-concurrent-mode branch 2 times, most recently from 19b5379 to f0b536f Compare December 19, 2022 09:01
@youknowriad
Copy link
Contributor

It still bugs me that in that PR Riad did so many updates that we didn't have to do in #45235 at all.

Some of these were for e2e tests and some others were due to actual testing (maybe Safari?) but I don't recall entirely, I'm sorry and if there are real issues, we'll discover them anyway. Maybe it's StrictMode (which I believe we should reintroduce at some point).

@jsnajdr jsnajdr force-pushed the enable/react-18-concurrent-mode branch from 3d20cb2 to aa9cd41 Compare January 30, 2023 12:13
@jsnajdr
Copy link
Member Author

jsnajdr commented Jan 30, 2023

I did some final updates to the initializeEditor APIs in the edit-post, edit-site and edit-widgets packages:

  1. Each of these packages exports an initializeEditor function that takes element id as parameter, plus options, it renders the editor to that id element and returns the React root object. Which can be used to root.unmount().
  2. The edit-widgets package calls this function initialize. To unify the API, I renamed it to initializeEditor, and kept the alternative initialize export, too.
  3. The reinitializeEditor functions are completely gone. I only kept compatibility noop exports that only report deprecation warnings.
  4. The functionality that attempts to "reboot" the editor when error boundary catches an error, it's gone, too. It used the reinitializeEditor functions passed as onError props to the top-level editor components.
  5. The error boundaries show the "Attempt Recovery" option only when an actual onError callback is passed. Because they are no longer passed, this feature is effectively disabled now.

@youknowriad @kevin940726 @tyxla please let me know what you think about this latest update. After we get an agreement on it, the PR can be immediately merged, just in time for WP 6.2.

@youknowriad
Copy link
Contributor

These changes make sense.

Each of these packages exports an initializeEditor function that takes element id as parameter, plus options, it renders the editor to that id element and returns the React root object. Which can be used to root.unmount().

Any reason for the returned value? I guess it's fine if we need it but we can also omit it?

The error boundaries show the "Attempt Recovery" option only when an actual onError callback is passed. Because they are no longer passed, this feature is effectively disabled now.

Can we remove that code entirely or maybe it's exposed?

@kevin940726
Copy link
Member

  1. The functionality that attempts to "reboot" the editor when error boundary catches an error, it's gone, too. It used the reinitializeEditor functions passed as onError props to the top-level editor components.
  2. The error boundaries show the "Attempt Recovery" option only when an actual onError callback is passed. Because they are no longer passed, this feature is effectively disabled now.

Any reason why we want to remove this functionality as well? I think this can still be implemented without a public reinitializeEditor API. NVM if we find this functionality not that helpful though 😅 .

The other parts LGTM 👍

@jsnajdr
Copy link
Member Author

jsnajdr commented Jan 30, 2023

Any reason for the returned value? I guess it's fine if we need it but we can also omit it?

@kevin940726 suggested it here: #46467 (comment) and it makes sense: if the function mounts some React UI, it should provide ability to unmount it. We don't use it anywhere so there's also a solid argument against it: let's not create future compatibility issues with something that's not even used.

@jsnajdr
Copy link
Member Author

jsnajdr commented Jan 30, 2023

Can we remove that code entirely or maybe it's exposed?

The only place where this is exposed is the ErrorBoundary component:

import { ErrorBoundary } from '@wordpress/editor'

This component has an optional onError prop, the callback to "Attempt Recovery". This prop would be removed.

That looks fine to be removed, doesn't it? onError prop can still be passed, but will be ignored.

@jsnajdr
Copy link
Member Author

jsnajdr commented Jan 30, 2023

Any reason why we want to remove this functionality as well? I think this can still be implemented without a public reinitializeEditor API.

Reinitializing the editor on error is just a random thing to try, throwing stuff at a wall and seeing if it sticks. It doesn't have a solid justification.

@tyxla
Copy link
Member

tyxla commented Jan 30, 2023

Reinitializing the editor on error is just a random thing to try, throwing stuff at a wall and seeing if it sticks. It doesn't have a solid justification.

Thinking out loud: a way to justify it could be avoiding page reloading to prevent the loss of any unsaved data.

@tyxla
Copy link
Member

tyxla commented Jan 30, 2023

I do agree that there's no guarantee that reloading the editor will help recover from a failure, but I've seen reinitialize working well sometimes, and that could prevent data loss if the user didn't save or autosave prior to the failure. I think we shouldn't rush to remove that functionality.

@youknowriad
Copy link
Contributor

That looks fine to be removed, doesn't it? onError prop can still be passed, but will be ignored.

Yeah, let's just remove. It's harmless indeed.

I've seen reinitialize working well sometimes, and that could prevent data loss if the user didn't save or autosave prior to the failure. I think we shouldn't rush to remove that functionality.

At the time, we implement this, we didn't have local storage persistence which we do today, so that argument doesn't hold I think.

@jsnajdr
Copy link
Member Author

jsnajdr commented Jan 30, 2023

Removed the onError props and "Attempt Recovery" buttons in 1638d6a.

@tyxla
Copy link
Member

tyxla commented Jan 30, 2023

At the time, we implement this, we didn't have local storage persistence which we do today, so that argument doesn't hold I think.

This is good to hear - I recall a complaint about data loss a few months ago, but I can't find a related issue. I think @mcsf reported it somewhere, but I can't find the convo. What you're saying makes sense to me, so let's try it out.

@@ -180,28 +173,24 @@ function Editor( {
}

return (
<StrictMode>
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should keep in mind to open a PR enabling StrictMode as soon as we land this one.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Strict mode enabler PR opened here: #47639

@jsnajdr
Copy link
Member Author

jsnajdr commented Jan 31, 2023

@youknowriad @tyxla @kevin940726 -- if all feedback is addressed, could someone approve this so that I can merge?

Copy link
Contributor

@youknowriad youknowriad left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Someone needs to click that 🟢 button

Copy link
Member

@tyxla tyxla left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've smoke-tested a bit both yesterday and today and didn't find any bugs. E2E tests are also passing, which can give us extra confidence. 🚀 Nice work out there!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
[Package] Element /packages/element [Type] Performance Related to performance efforts
Projects
No open projects
Development

Successfully merging this pull request may close these issues.

8 participants