diff --git a/.yarn/versions/a3c9f4be.yml b/.yarn/versions/a3c9f4be.yml
new file mode 100644
index 0000000..90948e7
--- /dev/null
+++ b/.yarn/versions/a3c9f4be.yml
@@ -0,0 +1,2 @@
+releases:
+ "@nytimes/react-prosemirror": patch
diff --git a/src/hooks/__tests__/useEditorViewLayoutEffect.test.tsx b/src/hooks/__tests__/useEditorViewLayoutEffect.test.tsx
index 20ce83e..10d6ca2 100644
--- a/src/hooks/__tests__/useEditorViewLayoutEffect.test.tsx
+++ b/src/hooks/__tests__/useEditorViewLayoutEffect.test.tsx
@@ -1,12 +1,17 @@
/* eslint-disable @typescript-eslint/no-empty-function */
import { render } from "@testing-library/react";
-import type { EditorState } from "prosemirror-state";
-import type { EditorView } from "prosemirror-view";
+import { Schema } from "prosemirror-model";
+import { EditorState } from "prosemirror-state";
+import { EditorView } from "prosemirror-view";
import React from "react";
import { LayoutGroup } from "../../components/LayoutGroup.js";
import { EditorContext } from "../../contexts/EditorContext.js";
+import {
+ setupProseMirrorView,
+ teardownProseMirrorView,
+} from "../../testing/setupProseMirrorView.js";
import { useEditorEffect } from "../useEditorEffect.js";
function TestComponent({
@@ -126,4 +131,101 @@ describe("useEditorViewLayoutEffect", () => {
expect(effect).toHaveBeenCalledTimes(2);
});
+
+ describe("with a real EditorView", () => {
+ const schema = new Schema({
+ nodes: {
+ text: {},
+ doc: { content: "text*" },
+ },
+ });
+
+ beforeAll(() => {
+ setupProseMirrorView();
+ });
+
+ afterAll(() => {
+ teardownProseMirrorView();
+ });
+
+ it("should not run the effect if the EditorView has been destroyed", () => {
+ const editorState = EditorState.create({ schema });
+ const mount = document.createElement("div");
+ document.body.appendChild(mount);
+ const editorView = new EditorView({ mount }, { state: editorState });
+
+ editorView.destroy();
+ expect(editorView.isDestroyed).toBe(true);
+
+ const effect = jest.fn();
+
+ render(
+
+ {},
+ unregisterEventListener: () => {},
+ }}
+ >
+
+
+
+ );
+
+ expect(effect).not.toHaveBeenCalled();
+
+ document.body.removeChild(mount);
+ });
+
+ it("should not re-run the effect after the EditorView is destroyed between renders", () => {
+ const editorState = EditorState.create({ schema });
+ const mount = document.createElement("div");
+ document.body.appendChild(mount);
+ const editorView = new EditorView({ mount }, { state: editorState });
+
+ const effect = jest.fn();
+
+ const { rerender } = render(
+
+ {},
+ unregisterEventListener: () => {},
+ }}
+ >
+
+
+
+ );
+
+ expect(effect).toHaveBeenCalledTimes(1);
+ expect(effect).toHaveBeenCalledWith(editorView);
+
+ editorView.destroy();
+ expect(editorView.isDestroyed).toBe(true);
+
+ rerender(
+
+ {},
+ unregisterEventListener: () => {},
+ }}
+ >
+
+
+
+ );
+
+ expect(effect).toHaveBeenCalledTimes(1);
+
+ document.body.removeChild(mount);
+ });
+ });
});
diff --git a/src/hooks/useEditorEffect.ts b/src/hooks/useEditorEffect.ts
index 007d61d..4c2e177 100644
--- a/src/hooks/useEditorEffect.ts
+++ b/src/hooks/useEditorEffect.ts
@@ -34,7 +34,7 @@ export function useEditorEffect(
// be defined inline and run on every re-render.
useLayoutGroupEffect(
() => {
- if (editorView) {
+ if (editorView && !editorView.isDestroyed) {
return effect(editorView);
}
},