️(frontend) improve unsubscribe logic when unmounting components

We can now unsubscribe on the editor events,
improving performance and preventing memory leaks.
This commit is contained in:
Anthony LC
2025-11-04 14:22:37 +01:00
parent ad16c0843c
commit c5d5d3dec4
4 changed files with 26 additions and 11 deletions

View File

@@ -12,7 +12,7 @@ import { BlockNoteView } from '@blocknote/mantine';
import '@blocknote/mantine/style.css'; import '@blocknote/mantine/style.css';
import { useCreateBlockNote } from '@blocknote/react'; import { useCreateBlockNote } from '@blocknote/react';
import { HocuspocusProvider } from '@hocuspocus/provider'; import { HocuspocusProvider } from '@hocuspocus/provider';
import { useEffect } from 'react'; import { useEffect, useRef } from 'react';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import * as Y from 'yjs'; import * as Y from 'yjs';
@@ -79,6 +79,7 @@ export const BlockNoteEditor = ({ doc, provider }: BlockNoteEditorProps) => {
const { setEditor } = useEditorStore(); const { setEditor } = useEditorStore();
const { t } = useTranslation(); const { t } = useTranslation();
const { isSynced: isConnectedToCollabServer } = useProviderStore(); const { isSynced: isConnectedToCollabServer } = useProviderStore();
const refEditorContainer = useRef<HTMLDivElement>(null);
useSaveDoc(doc.id, provider.document, isConnectedToCollabServer); useSaveDoc(doc.id, provider.document, isConnectedToCollabServer);
const { i18n } = useTranslation(); const { i18n } = useTranslation();
@@ -154,7 +155,9 @@ export const BlockNoteEditor = ({ doc, provider }: BlockNoteEditorProps) => {
); );
useHeadings(editor); useHeadings(editor);
useShortcuts(editor);
useShortcuts(editor, refEditorContainer.current);
useUploadStatus(editor); useUploadStatus(editor);
useEffect(() => { useEffect(() => {
@@ -166,7 +169,7 @@ export const BlockNoteEditor = ({ doc, provider }: BlockNoteEditorProps) => {
}, [setEditor, editor]); }, [setEditor, editor]);
return ( return (
<> <Box ref={refEditorContainer}>
{errorAttachment && ( {errorAttachment && (
<Box $margin={{ bottom: 'big', top: 'none', horizontal: 'large' }}> <Box $margin={{ bottom: 'big', top: 'none', horizontal: 'large' }}>
<TextErrors <TextErrors
@@ -186,7 +189,7 @@ export const BlockNoteEditor = ({ doc, provider }: BlockNoteEditorProps) => {
<BlockNoteSuggestionMenu /> <BlockNoteSuggestionMenu />
<BlockNoteToolbar /> <BlockNoteToolbar />
</BlockNoteView> </BlockNoteView>
</> </Box>
); );
}; };

View File

@@ -9,12 +9,13 @@ export const useHeadings = (editor: DocsBlockNoteEditor) => {
useEffect(() => { useEffect(() => {
setHeadings(editor); setHeadings(editor);
editor?.onChange(() => { const unsubscribe = editor?.onChange(() => {
setHeadings(editor); setHeadings(editor);
}); });
return () => { return () => {
resetHeadings(); resetHeadings();
unsubscribe();
}; };
}, [editor, resetHeadings, setHeadings]); }, [editor, resetHeadings, setHeadings]);
}; };

View File

@@ -2,7 +2,10 @@ import { useEffect } from 'react';
import { DocsBlockNoteEditor } from '../types'; import { DocsBlockNoteEditor } from '../types';
export const useShortcuts = (editor: DocsBlockNoteEditor) => { export const useShortcuts = (
editor: DocsBlockNoteEditor,
el: HTMLDivElement | null,
) => {
useEffect(() => { useEffect(() => {
const handleKeyDown = (event: KeyboardEvent) => { const handleKeyDown = (event: KeyboardEvent) => {
if (event.key === '@' && editor?.isFocused()) { if (event.key === '@' && editor?.isFocused()) {
@@ -29,11 +32,14 @@ export const useShortcuts = (editor: DocsBlockNoteEditor) => {
} }
}; };
// Attach the event listener to the document instead of the window if (!el) {
document.addEventListener('keydown', handleKeyDown); return;
}
el.addEventListener('keydown', handleKeyDown);
return () => { return () => {
document.removeEventListener('keydown', handleKeyDown); el.removeEventListener('keydown', handleKeyDown);
}; };
}, [editor]); }, [editor, el]);
}; };

View File

@@ -40,7 +40,7 @@ export const useUploadStatus = (editor: DocsBlockNoteEditor) => {
const { t } = useTranslation(); const { t } = useTranslation();
useEffect(() => { useEffect(() => {
editor.onChange((_, context) => { const unsubscribe = editor.onChange((_, context) => {
const blocksChanges = context.getChanges(); const blocksChanges = context.getChanges();
if (!blocksChanges.length) { if (!blocksChanges.length) {
@@ -90,7 +90,12 @@ export const useUploadStatus = (editor: DocsBlockNoteEditor) => {
return () => { return () => {
clearTimeout(timeoutId); clearTimeout(timeoutId);
unsubscribe();
}; };
}); });
return () => {
unsubscribe();
};
}, [editor, t]); }, [editor, t]);
}; };