bangle-editor icon indicating copy to clipboard operation
bangle-editor copied to clipboard

React Strict Mode: Error: It looks like renderHandlers were already set by someone else.

Open NimmLor opened this issue 3 years ago • 2 comments

The editor crashes with React 18 in development only, with the most basic setup. When building for production it works as expected.

const editorState = useEditorState({
  specRegistry:  new SpecRegistry([]),
})
return (
  <BangleEditor
     state={editorState}
  />
)
Uncaught Error: It looks like renderHandlers were already set by someone else.
    at saveRenderHandlers (index.js:774:15)
    at index.js:37:9
    at commitHookEffectListMount (react-dom.development.js:23150:26)
    at invokePassiveEffectMountInDEV (react-dom.development.js:25154:13)
    at invokeEffectsInDev (react-dom.development.js:27351:11)
    at commitDoubleInvokeEffectsInDEV (react-dom.development.js:27330:7)
    at flushPassiveEffectsImpl (react-dom.development.js:27056:5)
    at flushPassiveEffects (react-dom.development.js:26984:14)
    at react-dom.development.js:26769:9
    at workLoop (scheduler.development.js:266:34)

Using

"vite": "^3.2.2",
"react": "^18.2.0",
"react-dom": "^18.2.0",

UPDATE: It only crashes when <React.StrictMode> is used

NimmLor avatar Nov 12 '22 16:11 NimmLor

EDIT: Added a link to my working example below

Source Code Live Example

I do have a PR open to fix this issue, but for those of us that want to keep using React.StrictMode in the meantime I do have a workaround!

For those curious as to why this issue only occurs in StrictMode it causes react to invoke most hooks twice in order to detect potential side effects.

Workaround

Javascript being the wonderfully messed up language it is allows us to do the following.

Typescript

import { BangleEditor, useEditorState } from '@bangle.dev/react';

((has) => {
  WeakMap.prototype.has = function (key: WeakKey) {
    if (key instanceof HTMLDivElement && key.parentElement?.id === 'bangle-editor') return false;

    return has.bind(this)(key);
  };
})(WeakMap.prototype.has);

export function Editor() {
  const state = useEditorState({
    // ...
  });

  return (
    <div id="bangle-editor">
      <BangleEditor state={state} />
    </div>
  );
}

Javascript

import { BangleEditor, useEditorState } from '@bangle.dev/react';

((has) => {
  WeakMap.prototype.has = function (key) {
    if (key instanceof HTMLDivElement && key.parentElement?.id === 'bangle-editor') return false;

    return has.bind(this)(key);
  };
})(WeakMap.prototype.has);

export function Editor() {
  const state = useEditorState({
    // ...
  });

  return (
    <div id="bangle-editor">
      <BangleEditor state={state} />
    </div>
  );
}

cecilia-sanare avatar Feb 16 '24 14:02 cecilia-sanare