react-quill icon indicating copy to clipboard operation
react-quill copied to clipboard

TypeError: moduleClass is not a constructor in React-Quill with Quill Mention Module

Open Kuldeeptak-neelnetworks opened this issue 1 year ago • 11 comments

I'm encountering an error while integrating the quill-mention module with react-quill in a Next.js application. The error message I see is:

Unhandled Runtime Error TypeError: moduleClass is not a constructor

Call Stack SnowTheme.addModule (node_modules/react-quill/node_modules/quill/dist/quill.js (6130:0)) SnowTheme.addModule (node_modules/react-quill/node_modules/quill/dist/quill.js (6774:0)) eval (node_modules/react-quill/node_modules/quill/dist/quill.js (6122:0)) Array.forEach SnowTheme.init

Here is how I’m trying to use the quill-mention module: "use client"; import React, { Dispatch, SetStateAction, useEffect, useState } from "react"; import dynamic from "next/dynamic"; import Quill from "quill"; import Mention from "quill-mention"; import "react-quill/dist/quill.snow.css"; import { Button } from "@/components/ui/button"; import { Loader2 } from "lucide-react"; import TooltipCommon from "@/components/common/TooltipCommon"; import { useCopywriterStore } from "@/Store/CopywriterStore"; import { useUserStore } from "@/Store/UserStore"; import { useEditorStore } from "@/Store/EditorStore";

// Dynamically import ReactQuill to prevent SSR issues const ReactQuill = dynamic(() => import("react-quill"), { ssr: false });

interface QuillEditorProps { customerId: string | string[]; updateId: string | string[]; indicatorText: string | string[]; handleEdit: string | string[]; orderId: string | string[]; productFlowId: string | string[]; leadId: string | string[]; technicalId: string | string[]; copywriterId: string | string[]; amendmentId: string | string[]; websiteContentId: string | string[]; setIsOpenReplyModel: Dispatch<SetStateAction>; setOpenQuill: Dispatch<SetStateAction>; }

const QuillEditor: React.FC<QuillEditorProps> = ({ customerId, setIsOpenReplyModel, setOpenQuill, updateId, productFlowId, orderId, technicalId, leadId, indicatorText, amendmentId, copywriterId, websiteContentId, handleEdit, handlesave, editContent, }: any) => { const [value, setValue] = useState(""); const [images, setImages] = useState<File[]>([]); const [isLoading, setIsLoading] = useState(false); const [fileURLs, setFileURLs] = useState<{ [key: string]: string }>({}); const { fetchCopywriterData }: any = useCopywriterStore(); const { fetchUsersData, userData } = useUserStore(); const { fetchEditorData, fetchLeadsEditorData, fetchOrderEditorData, fetchTechnicalUpdateData, fetchAmendmentUpdateData, fetchProductFlowUpdateData, fetchWebsiteContentUpdateData, orderEditorData, fetchCopywriterUpdateData, editorData, addReplyData, addUpdateData, loading, }: any = useEditorStore();

const handleClear = () => { setValue(""); };

const handleChanges = (value: string, editorData: any) => { setValue(() => editorData.getText().trim() === "" && value === "" ? "" : value ); };

const handleAddData = async (e: React.FormEvent<HTMLFormElement>) => { e.preventDefault(); if ((value !== "" && value !== "


") || images.length > 0) { try { setIsLoading(true); const formData = new FormData(); formData.append("content", value); images.forEach((image) => formData.append("files", image)); const requests = [];
    if (indicatorText === "post") {
      requests.push(
        orderId && baseInstance.post(`/updates/order/${orderId}`, formData),
        productFlowId &&
          baseInstance.post(
            `/updates/productflow/${productFlowId}`,
            formData
          ),
        customerId &&
          baseInstance.post(`/updates/customer/${customerId}`, formData),
        leadId && baseInstance.post(`/updates/lead/${leadId}`, formData),
        technicalId &&
          baseInstance.post(
            `/updates/technicaltracker/${technicalId}`,
            formData
          ),
        copywriterId &&
          baseInstance.post(
            `/updates/copywritertracker/${copywriterId}`,
            formData
          ),
        websiteContentId &&
          baseInstance.post(
            `/updates/newwebsitecontent/${websiteContentId}`,
            formData
          ),
        amendmentId &&
          baseInstance.post(`/updates/amendment/${amendmentId}`, formData)
      );
    }

    if (indicatorText === "reply") {
      requests.push(
        baseInstance.post(`/updates/update/reply/${updateId}`, formData)
      );
    }

    const responses = await Promise.all(requests.filter(Boolean));
    responses.forEach((response) => {
      if (response.status === 201) {
        successToastingFunction(response?.data?.message);
        fetchEditorData(customerId);
        fetchOrderEditorData(orderId);
        fetchLeadsEditorData(leadId);
        fetchTechnicalUpdateData(technicalId);
        fetchCopywriterUpdateData(copywriterId);
        fetchAmendmentUpdateData(amendmentId);
        fetchProductFlowUpdateData(productFlowId);
        fetchWebsiteContentUpdateData(websiteContentId);
        setIsOpenReplyModel(false);
        setOpenQuill(false);
        handleClear();
        setImages([]);
        setValue("");
      }
      fetchEditorData(customerId);
    });
  } catch (error) {
    errorToastingFunction(error);
  } finally {
    setIsLoading(false);
  }
} else {
  errorToastingFunction("Please enter text or upload an image to submit.");
}

};

const handleFileUpload = (files: FileList | null) => { if (files) { const fileList = Array.from(files); setImages((prevImages) => [...prevImages, ...fileList]);

  fileList.forEach((file) => {
    if (file.type.startsWith("image/")) {
      const reader = new FileReader();
      reader.onloadend = () => {
        const img = `<img src="${reader.result}" alt="${file.name}" />`;
        setValue((prevValue) => prevValue + img + `</br>`);
      };
      reader.readAsDataURL(file);
    } else {
      const url = URL.createObjectURL(file);
      setFileURLs((prev) => ({ ...prev, [file.name]: url }));
      const link = `<a href="${url}" target="_blank" rel="noopener noreferrer">${file.name}</a>`;
      setValue((prevValue) => prevValue + link + `</br>`);
    }
  });
}

};

const imageHandler = () => { const inputImage = document.createElement("input"); inputImage.setAttribute("type", "file"); inputImage.setAttribute( "accept", "image/, video/, .pdf, .xlsx, .doc, .docx" ); inputImage.setAttribute("multiple", "true"); inputImage.click();

inputImage.onchange = (e) => {
  handleFileUpload(inputImage.files);
};

};

useEffect(() => { return () => { Object.values(fileURLs).forEach((url) => URL.revokeObjectURL(url)); }; }, [fileURLs]);

// Commented out because it causes an error // useEffect(() => { // if (Quill) { // Quill.register("modules/imageResize", ImageResize); // Quill.register("modules/mention", Mention); // } // }, [Quill]);

const toolbarOptions = [ ["bold", "italic", "underline", "strike"], [{ size: ["small", false, "large", "huge"] }], [{ list: "ordered" }, { list: "bullet" }, { list: "check" }], [{ script: "sub" }, { script: "super" }], [{ indent: "-1" }, { indent: "+1" }], [{ direction: "rtl" }], [{ color: [] }, { background: [] }], [{ font: [] }], [{ align: [] }], ];

const options = { debug: "info", modules: { toolbar: toolbarOptions, imageResize: { parchment: Quill.import("parchment"), modules: ["Resize", "DisplaySize"], }, mention: { allowedChars: /^[A-Za-z\s]*$/, mentionDenotationChars: ["@"], source: async ( searchTerm: string, renderItem: (data: any[]) => void ) => { try { await fetchUsersData(); // Ensure data is fetched first const filteredUsers = userData .filter((user: any) => user.name.toLowerCase().includes(searchTerm.toLowerCase()) ) .map((user: any) => ({ value: user.name })); renderItem(filteredUsers); } catch (error) { console.error("Error fetching mention users:", error); renderItem([]); } }, }, }, placeholder: "Compose an epic...", theme: "snow", };

return ( <> <form onSubmit={handleAddData} className="flex gap-2 flex-col "> <div className=""> <ReactQuill placeholder={options.placeholder} theme={options.theme} modules={options.modules} value={value} onChange={(value, _, __, editor) => { handleChanges(value, editor); }} /> <div className="flex justify-start gap-2 items-center"> <Button type="submit" className="cursor-pointer h-[24px] rounded-lg border border-primary bg-primary px-4 text-white transition hover:bg-opacity-90" > {isLoading ? ( <Loader2 className="mr-2 h-6 w-6 animate-spin text-[#fff]" /> ) : indicatorText === "reply" ? ( "Reply" ) : ( "Update" )} </Button>

      <div onClick={imageHandler} className="w-fit cursor-pointer ">
        <TooltipCommon text="Add Files">
          <div className="hover:bg-gray-100 px-2 py-1 rounded">
            <AddFilesDarkUIconSVG />
          </div>
        </TooltipCommon>
      </div>
    </div>
  </form>
</>

); };

export default QuillEditor;

Question:

I've integrated the quill-mention module into my react-quill component but am running into an issue. The error TypeError: moduleClass is not a constructor appears, and the call stack indicates a problem with SnowTheme.addModule in the Quill.js file.

I've tried to register the module as follows: useEffect(() => { if (Quill) { Quill.register("modules/imageResize", ImageResize); Quill.register("modules/mention", Mention); } }, [Quill]);

However, this causes the mentioned error. I've also commented out the registration as it seems to be the cause of the issue.

What I've Tried:

Ensuring that the quill-mention module is correctly imported and used. Checking the version compatibility of react-quill, quill, and quill-mention. Referencing the Quill documentation for proper module registration. What I Need Help With:

Why am I encountering this TypeError? How can I properly integrate the quill-mention module with react-quill? Are there any additional steps or configurations required to avoid this error? Any guidance or suggestions would be greatly appreciated!

This is the Error:- image

Kuldeeptak-neelnetworks avatar Sep 05 '24 09:09 Kuldeeptak-neelnetworks

"quill": "^2.0.2", "quill-image-resize-module-react": "^3.0.0", "quill-image-resize-module-ts": "^3.0.3", "quill-mention": "^6.0.1", "react-quill": "^2.0.0",

these are the versions.
package,json

Kuldeeptak-neelnetworks avatar Sep 05 '24 09:09 Kuldeeptak-neelnetworks

I'm also facing same issue. Let me know if any solution found?

kaushik-codal avatar Sep 14 '24 09:09 kaushik-codal

hello did you find the solution?? I am still searching but didn't find.. should we change in node Modules package.?

Kuldeeptak-neelnetworks avatar Sep 19 '24 05:09 Kuldeeptak-neelnetworks

Unhandled Runtime Error TypeError: Cannot read properties of undefined (reading 'unshift')

Call Stack new Mention (app-pages-browser)\node_modules\quill-mention\dist\mention.mjs (57:36) SnowTheme.addModule (app-pages-browser)\node_modules\react-quill\node_modules\quill\dist\quill.js (6130:0) SnowTheme.addModule (app-pages-browser)\node_modules\react-quill\node_modules\quill\dist\quill.js (6774:0) eval (app-pages-browser)\node_modules\react-quill\node_modules\quill\dist\quill.js (6122:0) Array.forEach SnowTheme.init (app-pages-browser)\node_modules\react-quill\node_modules\quill\dist\quill.js (6120:0) new Quill (app-pages-browser)\node_modules\react-quill\node_modules\quill\dist\quill.js (1163:0)

Kuldeeptak-neelnetworks avatar Sep 19 '24 07:09 Kuldeeptak-neelnetworks

"quill": "^2.0.2", "quill-image-resize-module-react": "^3.0.0", "quill-image-resize-module-ts": "^3.0.3", "quill-mention": "^6.0.1",

Kuldeeptak-neelnetworks avatar Sep 19 '24 07:09 Kuldeeptak-neelnetworks

Unhandled Runtime Error TypeError: Cannot read properties of undefined (reading 'unshift')

Call Stack new Mention (app-pages-browser)\node_modules\quill-mention\dist\mention.mjs (57:36) SnowTheme.addModule (app-pages-browser)\node_modules\react-quill\node_modules\quill\dist\quill.js (6130:0) SnowTheme.addModule (app-pages-browser)\node_modules\react-quill\node_modules\quill\dist\quill.js (6774:0) eval (app-pages-browser)\node_modules\react-quill\node_modules\quill\dist\quill.js (6122:0) Array.forEach SnowTheme.init (app-pages-browser)\node_modules\react-quill\node_modules\quill\dist\quill.js (6120:0) new Quill (app-pages-browser)\node_modules\react-quill\node_modules\quill\dist\quill.js (1163:0)

could you please mention me when you got the solution same error facing

zakriayounas avatar Oct 17 '24 10:10 zakriayounas

+1 Also having this issue with these versions of react-quill and quill-mention. quill Cannot import modules/mention. Are you sure it was registered? Uncaught TypeError: moduleClass is not a constructor

Annastrin avatar Oct 23 '24 06:10 Annastrin

+1 Also having this issue with these versions of react-quill and quill-mention. quill Cannot import modules/mention. Are you sure it was registered? Uncaught TypeError: moduleClass is not a constructor

+1 Having the same issue, do let me know if you find the solution.

iamdipansh avatar Oct 23 '24 09:10 iamdipansh

i'm facing this issue

Cannot read properties of undefined (reading 'unshift')

any updates?

dreamerchandra avatar Nov 24 '24 01:11 dreamerchandra

going to "quill-mention": "^4.1.0", fixed this for me

creatordigital-paul avatar Jan 02 '25 03:01 creatordigital-paul

From https://github.com/VaguelySerious/react-quill (a fork of this repo):

This is fork of react-quill that updates its QuillJS dependency from 1.3.7 to >=2.0.2 and tries to keep on top of dependency updates and issues, as the original maintainers are no longer active.

cundejo avatar Oct 22 '25 17:10 cundejo