Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Open
Kuldeeptak-neelnetworks opened this issue Sep 5, 2024 · 5 comments

Comments

@Kuldeeptak-neelnetworks
Copy link

Kuldeeptak-neelnetworks commented Sep 5, 2024

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 = ({
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) => {
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 (
<>



<ReactQuill
placeholder={options.placeholder}
theme={options.theme}
modules={options.modules}
value={value}
onChange={(value, _, __, editor) => {
handleChanges(value, editor);
}}
/>



{isLoading ? (

) : indicatorText === "reply" ? (
"Reply"
) : (
"Update"
)}

      <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
Copy link
Author

"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

@kaushik-codal
Copy link

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

@Kuldeeptak-neelnetworks
Copy link
Author

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

@Kuldeeptak-neelnetworks
Copy link
Author

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
Copy link
Author

"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",

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants