From 2afcb91d244a72693a010698ede407bc0fc1c930 Mon Sep 17 00:00:00 2001 From: IanM Date: Tue, 12 May 2026 21:45:51 +0100 Subject: [PATCH] [2.x] fix: TextEditor.onbuild race against async Mithril redraw MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `oncreate` called `_load().then(() => setTimeout(this.onbuild, 50))`. The 50ms timer is a guess at how long it takes Mithril's async `m.redraw()` to flush — under slow first-load conditions (cold CDN, heavy DOM, additional `_loaders`) the redraw can land *after* the timer fires, leaving `this.$('.TextEditor-editorContainer')[0]` as `undefined` when `onbuild` runs. That `undefined` then propagates into the editor driver (notably breaking Tiptap-based drivers like fof/rich-text, where it produces a non-functional toolbar with no editor underneath). Use `m.redraw.sync()` in `_load` so the DOM is updated before the promise resolves, and drop the 50ms timer entirely. The `.then` runs on a microtask outside any Mithril event handler, so `redraw.sync()` is safe here. Closes #4612 --- framework/core/js/src/common/components/TextEditor.js | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/framework/core/js/src/common/components/TextEditor.js b/framework/core/js/src/common/components/TextEditor.js index 249d60eb8d..4c41e80fd0 100644 --- a/framework/core/js/src/common/components/TextEditor.js +++ b/framework/core/js/src/common/components/TextEditor.js @@ -72,9 +72,7 @@ export default class TextEditor extends Component { oncreate(vnode) { super.oncreate(vnode); - this._load().then(() => { - setTimeout(this.onbuild.bind(this), 50); - }); + this._load().then(this.onbuild.bind(this)); } onbuild() { @@ -96,7 +94,12 @@ export default class TextEditor extends Component { _load() { return Promise.all(this._loaders.map((loader) => loader())).then(() => { this.loading = false; - m.redraw(); + // Synchronous redraw so the editor container is in the DOM before + // `onbuild` runs. Mithril's regular `m.redraw()` queues for the next + // frame, which previously raced against a 50ms setTimeout and could + // leave `this.$('.TextEditor-editorContainer')[0]` undefined on slow + // first loads. See flarum/framework#4612. + m.redraw.sync(); }); }