I’m trying to use react-quill
in my nextJS
project but am getting this error :
1 of 1 unhandled error
Unhandled Runtime Error
TypeError: moduleClass is not a constructor
Call Stack
SnowTheme.addModule
node_modulesquilldistquill.js (6130:0)
SnowTheme.addModule
node_modulesquilldistquill.js (6774:0)
eval
node_modulesquilldistquill.js (6122:0)
Array.forEach
<anonymous>
SnowTheme.init
node_modulesquilldistquill.js (6120:0)
new Quill
node_modulesquilldistquill.js (1163:0)
ReactQuill.createEditor
node_modulesreact-quilllibindex.js (223:0)
ReactQuill.instantiateEditor
node_modulesreact-quilllibindex.js (187:0)
ReactQuill.componentDidMount
node_modulesreact-quilllibindex.js (152:0)
commitLifeCycles
node_modulesreact-domcjsreact-dom.development.js (20663:0)
commitLayoutEffects
node_modulesreact-domcjsreact-dom.development.js (23426:0)
HTMLUnknownElement.callCallback
node_modulesreact-domcjsreact-dom.development.js (3945:0)
Object.invokeGuardedCallbackDev
node_modulesreact-domcjsreact-dom.development.js (3994:0)
invokeGuardedCallback
node_modulesreact-domcjsreact-dom.development.js (4056:0)
commitRootImpl
node_modulesreact-domcjsreact-dom.development.js (23151:0)
unstable_runWithPriority
node_modulesschedulercjsscheduler.development.js (468:0)
runWithPriority$1
node_modulesreact-domcjsreact-dom.development.js (11276:0)
commitRoot
node_modulesreact-domcjsreact-dom.development.js (22990:0)
performSyncWorkOnRoot
node_modulesreact-domcjsreact-dom.development.js (22329:0)
eval
node_modulesreact-domcjsreact-dom.development.js (11327:0)
unstable_runWithPriority
node_modulesschedulercjsscheduler.development.js (468:0)
runWithPriority$1
node_modulesreact-domcjsreact-dom.development.js (11276:0)
flushSyncCallbackQueueImpl
node_modulesreact-domcjsreact-dom.development.js (11322:0)
flushSyncCallbackQueue
node_modulesreact-domcjsreact-dom.development.js (11309:0)
scheduleUpdateOnFiber
node_modulesreact-domcjsreact-dom.development.js (21893:0)
dispatchAction
node_modulesreact-domcjsreact-dom.development.js (16139:0)
checkForUpdates
node_modulesuse-subscriptioncjsuse-subscription.development.js (85:0)
eval
node_modulesnextdistsharedlibloadable.js (183:44)
Set.forEach
<anonymous>
LoadableSubscription._update
node_modulesnextdistsharedlibloadable.js (183:24)
eval
node_modulesnextdistsharedlibloadable.js (164:17)
and here is the code shows how imported It and how I rendered it :
import React, { useState, useEffect, useCallback } from 'react';
import 'react-quill/dist/quill.snow.css';
import { Box, TextField, Button, Typography, Input } from '@mui/material';
import { getStorage, ref, uploadBytes, getDownloadURL } from 'firebase/storage';
import { getFirestore, collection, addDoc } from 'firebase/firestore';
import dynamic from 'next/dynamic';
const ReactQuill = dynamic(() => import('react-quill'), { ssr: false, loading: () => <p>Loading...</p> });
const Quill = dynamic(() => import('react-quill'), { ssr: false, loading: () => <p>Loading...</p> });
const ImageUploader = dynamic(() => import('quill-image-uploader'), { ssr: false, loading: () => <p>Loading...</p> });
const CreatePost = () => {
const [titles, setTitles] = useState({
title_ar: '',
title_en: '',
title_fr: '',
title_tr: '',
});
const [body, setBody] = useState('');
const [mainImage, setMainImage] = useState(null);
const [uploading, setUploading] = useState(false);
const storage = getStorage();
const db = getFirestore();
useEffect(() => {
if (Quill && Quill.register) {
Quill.register('modules/imageUploader', ImageUploader);
}
}, []);
const handleTitleChange = useCallback((e) => {
const { name, value } = e.target;
setTitles((prevTitles) => ({ ...prevTitles, [name]: value }));
}, []);
const handleEditorChange = useCallback((content) => {
setBody(content);
}, []);
const handleMainImageChange = (e) => {
setMainImage(e.target.files[0]);
};
const uploadImage = async (image) => {
if (!image) {
throw new Error('No image provided');
}
const storageRef = ref(storage, `images/${image.name}`);
await uploadBytes(storageRef, image);
return getDownloadURL(storageRef);
};
const uploadInBodyImage = async (image) => {
if (!image) {
return Promise.reject(new Error('No image provided'));
}
const storageRef = ref(storage, `images/${image.name}`);
return uploadBytes(storageRef, image).then(() => {
return getDownloadURL(storageRef).then((url) => {
return url;
});
}).catch((error) => {
console.error('Error uploading image or getting URL:', error);
throw error;
});
};
const modules = {
toolbar: [
[{ 'header': '1'}, {'header': '2'}, { 'font': [] }],
[{ 'list': 'ordered'}, { 'list': 'bullet' }],
['bold', 'italic', 'underline'],
['link', 'image'],
['clean']
],
imageUploader: {
upload: async (file) => {
try {
const url = await uploadInBodyImage(file);
return url;
} catch (error) {
console.error('Image upload failed:', error);
throw error;
}
},
},
};
const handleSubmit = async (e) => {
e.preventDefault();
setUploading(true);
try {
let mainImageUrl = '';
if (mainImage) {
mainImageUrl = await uploadImage(mainImage);
}
await addDoc(collection(db, 'Article'), {
...titles,
body,
mainImageUrl,
createdAt: new Date(),
});
alert('Article created successfully!');
setTitles({
title_ar: '',
title_en: '',
title_fr: '',
title_tr: '',
});
setBody('');
setMainImage(null);
} catch (error) {
console.error('Error creating article:', error);
alert('Failed to create article.');
} finally {
setUploading(false);
}
};
return (
<Box sx={{ p: 3 }}>
<Typography variant="h5" gutterBottom>Create a New Article</Typography>
<form onSubmit={handleSubmit}>
<Box mb={2}>
<TextField
fullWidth
label="Title (Arabic)"
name="title_ar"
value={titles.title_ar}
onChange={handleTitleChange}
variant="outlined"
/>
</Box>
<Box mb={2}>
<TextField
fullWidth
label="Title (English)"
name="title_en"
value={titles.title_en}
onChange={handleTitleChange}
variant="outlined"
/>
</Box>
<Box mb={2}>
<TextField
fullWidth
label="Title (French)"
name="title_fr"
value={titles.title_fr}
onChange={handleTitleChange}
variant="outlined"
/>
</Box>
<Box mb={2}>
<TextField
fullWidth
label="Title (Turkish)"
name="title_tr"
value={titles.title_tr}
onChange={handleTitleChange}
variant="outlined"
/>
</Box>
<Box mb={2}>
<Typography variant="h6" gutterBottom>Body</Typography>
<ReactQuill
value={body}
onChange={handleEditorChange}
modules={modules}
style={{ height: '400px' }}
/>
</Box>
<Box mb={2}>
<Input
type="file"
accept="image/*"
onChange={handleMainImageChange}
fullWidth
/>
</Box>
<Button type="submit" variant="contained" color="primary" disabled={uploading}>
{uploading ? 'Uploading...' : 'Create Article'}
</Button>
</form>
</Box>
);
};
export default CreatePost;