Files
MyClub/DOCS/QUILL_EMITTER_ERROR_FIX.md
Tomas Dvorak 087f30e82c dev day #80
2025-11-02 21:31:00 +01:00

3.2 KiB

Quill.js Emitter Error Fix

Issue

Uncaught TypeError: can't access property "emit", this.emitter is undefined

This error occurred in CustomRichEditor.tsx when Quill.js tried to initialize or perform operations before its internal emitter was ready.

Root Cause

  1. Unstable module configuration - The handleImageUpload callback was included in quillModules dependencies, causing the modules object to recreate on every render
  2. Missing initialization guards - Code attempted to access Quill editor methods before the editor was fully initialized
  3. Concurrent DOM mutations - MutationObserver showed Quill was initializing while DOM was being modified

Solution Applied

1. Stabilized Image Upload Handler

Before:

const handleImageUpload = useCallback(() => { ... }, []);

const quillModules = useMemo(() => ({
  toolbar: {
    handlers: {
      image: onImageUpload ? handleImageUpload : undefined,
    },
  },
}), [toolbarConfig, onImageUpload, handleImageUpload]); // handleImageUpload caused recreation

After:

const handleImageUploadRef = useRef<() => void>();
  
useEffect(() => {
  handleImageUploadRef.current = () => { ... };
});

const quillModules = useMemo(() => ({
  toolbar: {
    handlers: {
      image: onImageUpload ? () => handleImageUploadRef.current?.() : undefined,
    },
  },
}), [toolbarConfig, onImageUpload]); // Only stable dependencies

2. Added Emitter Safety Checks

Before:

const quill = quillRef.current?.getEditor();
if (quill) {
  quill.focus();
  // ... operations
}

After:

const quill = quillRef.current?.getEditor();
if (quill && quill.root && quill.emitter) {
  setTimeout(() => {
    // Double-check Quill is still valid
    if (!quill || !quill.emitter) {
      toast({ title: 'Editor není připraven', ... });
      return;
    }
    // ... operations
  }, 100);
} else {
  toast({ title: 'Editor není připraven', ... });
}

3. Added Stable Key to ReactQuill

<ReactQuill
  key={`quill-${readOnly ? 'readonly' : 'edit'}`}
  // ... other props
/>

This prevents unnecessary remounting while allowing controlled reinitialization when mode changes.

4. Protected Image Manipulation Effect

useEffect(() => {
  const editor = quillRef.current?.getEditor();
  if (!editor || !editor.root || !editor.emitter || readOnly) return;
  // ... event handlers
}, [readOnly, toast]);

Benefits

  • Prevents Quill from reinitializing on every render
  • Ensures operations only happen when editor is fully ready
  • Provides user feedback when editor isn't ready
  • Maintains stable component lifecycle
  • Fixes the "this.emitter is undefined" error

Testing

  1. Create a new article in admin panel
  2. Click "Vložit obrázek" or use toolbar image button
  3. Select and crop an image
  4. Verify image inserts without errors
  5. Test image editing features (resize, filters, alignment)
  6. Check browser console for absence of Quill errors

Files Modified

  • frontend/src/components/common/CustomRichEditor.tsx