Skip to content

[React Editor] Cannot read properties of null (reading 'parentNode') in StrictMode #994

@martin-fleck-at

Description

@martin-fleck-at

I have a scenario where in React StrictMode it can happen that the containerRef.current we use to start the editor app is actually null. I assume that it is a race condition between the StrictMode double-mount and the global queue.

  • First mount schedules editorInit via addQueue('editorInit', editorInit).
  • React immediately unmounts the component (StrictMode dev behavior), sets containerRef.current = null.
  • The queued editorInit runs after that unmount, so at: await editorAppRef.current.start(containerRef.current!);
    containerRef.current is null → error

If I slow it down by debugging on a breakpoint, it often works because the queue sometimes runs after the second mount, where React has already set containerRef.current to the new DOM node. So it seems that the timing decides whether we get null or not.

It is hard to reproduce for me in a test scenario but in my use case I can easily see it. If I don't use StrictMode, everything works as expected.

I can see the following in my log:

log.js:155 DEBUG CONFIG PROCESSED: 1763120903595
log.js:155 DEBUG CONFIG PROCESSED: Done: 1763120903596
log.js:155 DEBUG >>>>>>>>>>>>>>>>>>>>>>>>>>>>>
log.js:155 DEBUG Adding to queue: editorInit: QUEUE SIZE before: 0
log.js:155 DEBUG >>>>>>>>>>>>>>>>>>>>>>>>>>>>>
log.js:155 DEBUG Adding to queue: disposeEditor: QUEUE SIZE before: 1
log.js:155 DEBUG CONFIG PROCESSED: 1763120903598
log.js:155 DEBUG CONFIG PROCESSED: Done: 1763120903598
log.js:155 DEBUG >>>>>>>>>>>>>>>>>>>>>>>>>>>>>
log.js:155 DEBUG Adding to queue: editorInit: QUEUE SIZE before: 2
log.js:155 DEBUG Checking queue...false
index.tsx:53 Queue size: 3
log.js:155 DEBUG <<<<<<<<<<<<<<<<<<<<<<<<<<<<<
log.js:155 DEBUG QUEUE editorInit start: SIZE before: 3: 1763120903623
log.js:155 DEBUG INIT EDITOR: 1763120903623
log.js:155 DEBUG INIT: Creating editor: 1763120903623
log.js:155 DEBUG INIT EDITOR: Done: 1763120903677
log.js:155 DEBUG QUEUE editorInit end: SIZE after: 2
log.js:155 DEBUG <<<<<<<<<<<<<<<<<<<<<<<<<<<<<
log.js:155 DEBUG QUEUE disposeEditor start: SIZE before: 2: 1763120903681
log.js:155 DEBUG DISPOSE: 1763120903681
log.js:155 DEBUG >>>>>>>>>>>>>>>>>>>>>>>>>>>>>
log.js:155 DEBUG Adding to queue: disposeEditor: QUEUE SIZE before: 1
log.js:155 DEBUG DISPOSE DONE: 1763120903688
log.js:155 DEBUG QUEUE disposeEditor end: SIZE after: 2
log.js:155 DEBUG <<<<<<<<<<<<<<<<<<<<<<<<<<<<<
log.js:155 DEBUG QUEUE editorInit start: SIZE before: 2: 1763120903688
log.js:155 DEBUG INIT EDITOR: 1763120903688
log.js:155 DEBUG INIT: Creating editor: 1763120903688
log.js:155 DEBUG ERROR: Cannot read properties of null (reading 'parentNode'): 1763120903689
log.js:155 DEBUG INTERCEPTED Error: TypeError: Cannot read properties of null (reading 'parentNode'). Stopping queue...
dom.js:578 Uncaught (in promise) TypeError: Cannot read properties of null (reading 'parentNode')
    at getShadowRoot (dom.js:578:20)
    at isInShadowDOM (dom.js:575:14)
    at StandaloneThemeService.registerEditorContainer (standaloneThemeService.js:222:13)
    at new StandaloneEditor2 (standaloneCodeEditor.js:165:51)
    at new <anonymous> (injection.js:16:13)
    at new ConfiguredStandaloneEditor (monaco.js:86:13)
    at _InstantiationService._createInstance (instantiationService.js:129:24)
    at _InstantiationService.createInstance (instantiationService.js:103:27)
    at Object.createConfiguredEditor [as create] (monaco.js:298:10)
    at EditorApp.createEditors (editorApp.ts:220:41)
getShadowRoot @ dom.js:578
isInShadowDOM @ dom.js:575
registerEditorContainer @ standaloneThemeService.js:222
StandaloneEditor2 @ standaloneCodeEditor.js:165
(anonymous) @ injection.js:16
ConfiguredStandaloneEditor @ monaco.js:86
_createInstance @ instantiationService.js:129
createInstance @ instantiationService.js:103
createConfiguredEditor @ monaco.js:298
createEditors @ editorApp.ts:220
await in createEditors
start @ editorApp.ts:171
editorInit @ index.tsx:213
executeQueue @ index.tsx:62
await in executeQueue
(anonymous) @ index.tsx:74
setInterval
kickQueue @ index.tsx:71
addQueue @ index.tsx:49
(anonymous) @ index.tsx:322
react_stack_bottom_frame @ react-dom-client.development.js:23953
runWithFiberInDEV @ react-dom-client.development.js:1519
commitHookEffectListMount @ react-dom-client.development.js:11905
commitHookPassiveMountEffects @ react-dom-client.development.js:12026
reconnectPassiveEffects @ react-dom-client.development.js:14004
recursivelyTraverseReconnectPassiveEffects @ react-dom-client.development.js:13976
commitPassiveMountOnFiber @ react-dom-client.development.js:13936
recursivelyTraversePassiveMountEffects @ react-dom-client.development.js:13815
commitPassiveMountOnFiber @ react-dom-client.development.js:13903
recursivelyTraversePassiveMountEffects @ react-dom-client.development.js:13815
commitPassiveMountOnFiber @ react-dom-client.development.js:13957
recursivelyTraversePassiveMountEffects @ react-dom-client.development.js:13815
[...]
log.js:155 DEBUG Checking queue...false
index.tsx:53 Queue size: 1
log.js:155 DEBUG <<<<<<<<<<<<<<<<<<<<<<<<<<<<<
log.js:155 DEBUG QUEUE disposeEditor start: SIZE before: 1: 1763120903693
log.js:155 DEBUG DISPOSE: 1763120903693
log.js:155 DEBUG DISPOSE DONE: 1763120903693
log.js:155 DEBUG QUEUE disposeEditor end: SIZE after: 0
log.js:155 DEBUG Stopping queue...

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions