d4rkr00t / prosemirror-dev-tools

Developer Tools for ProseMirror
316 stars 37 forks source link

Incompatibility with fast refresh in Next.js #113

Open kachkaev opened 2 years ago

kachkaev commented 2 years ago

šŸ‘‹ @d4rkr00t! Thanks a lot for this awesome package!

Iā€™d like to share one issue I could not resolve myself, it is to do with React fast refresh in Next.js. Here is the MWE: https://codesandbox.io/s/prosemirror-dev-tools-hot-reloading-mwe-d7osb?file=/pages/index.jsx

https://user-images.githubusercontent.com/608862/140981365-19c686fb-52ee-46d3-870f-e7e83a837cb5.mp4

Scenario 1

  1. Open pages/index.jsx in dev mode

  2. Trigger fast refresh (e.g. add comment and save)

  3. Observe ProseMirror DevTools icon disappearing Expected: It stays up

  4. Trigger fast refresh again

  5. Observe ProseMirror DevTools icon re-appearing

Scenario 2

  1. Open pages/index.jsx in dev mode
  2. Bring up ProseMirror DevTools by clicking on the icon
  3. Trigger fast refresh
  4. Observe

    Unhandled Runtime Error
    NotFoundError: Node.removeChild: The node to be removed is not a child of this node

    Expected: No crash

Console output in both scenarios

Warning: componentWillReceiveProps has been renamed, and is not recommended for use. See https://reactjs.org/link/unsafe-component-lifecycles for details.

* Move data fetching code or side effects to componentDidUpdate.
* If you're updating state whenever props change, refactor your code to use memoization techniques or move it to static getDerivedStateFromProps. Learn more at: https://reactjs.org/link/derived-state
* Rename componentWillReceiveProps to UNSAFE_componentWillReceiveProps to suppress this warning in non-strict mode. In React 18.x, only the UNSAFE_ name will work. To rename all deprecated lifecycles to their new names, you can run `npx react-codemod rename-unsafe-lifecycles` in your project source folder.

Please update the following components: Dock, JSONNestedNode, JSONTree, NodePicker
Warning: render(...): It looks like the React-rendered content of this container was removed without using React. This is not supported and will cause errors. Instead, call ReactDOM.unmountComponentAtNode to empty a container.
Home@webpack-internal:///./pages/index.jsx:33:67
App@webpack-internal:///./node_modules/next/dist/pages/_app.js:152:24
StyleRegistry@webpack-internal:///./node_modules/styled-jsx/dist/stylesheet-registry.js:231:28
ErrorBoundary@webpack-internal:///./node_modules/@next/react-dev-overlay/lib/internal/ErrorBoundary.js:26:47
ReactDevOverlay@webpack-internal:///./node_modules/@next/react-dev-overlay/lib/internal/ReactDevOverlay.js:90:20
Container@webpack-internal:///./node_modules/next/dist/client/index.js:305:24
AppContainer@webpack-internal:///./node_modules/next/dist/client/index.js:744:20
Root@webpack-internal:///./node_modules/next/dist/client/index.js:865:21

Any thoughts are welcome šŸ™Œ

nathggns commented 2 years ago

I can repro this. Would love some advice on addressing it!

d4rkr00t commented 2 years ago

Had a quick look in the morning, seems like need a bit more time to investigate. I'll try and spend some time this week.

mattcasey commented 2 years ago

Hi, I ran into this error on a Next.js app and see the warnings as well. I'm using bangle.dev as a wrapper and have a special component that loads the dev tools because it's the only way I can access the view but I think the issue is the same. I fixed this by moving the call to applyDevTools(view) into a useEffect hook that watches the view. (In my example, Bangle.dev exposes the editor view using context):

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

export default function Editor() { 
  const editorState = useEditorState({ initialValue: 'Hello world!'  });
  return (
    <BangleEditor state={editorState}>
      <RegisterDevTools />
    </BangleEditor>
  );
}

# fails when editor is re-rendered
function RegisterDevTools () {
  const view = useEditorViewContext();
  applyDevTools(view);
  return null;
}

# no error
function RegisterDevTools () {
  const view = useEditorViewContext();
  useEffect(() => {
    applyDevTools(view);
  }, [view]);
  return null;
}

Hope this helps someone