Next.js + TypeScript项目中React Quill自定义图片处理器的类型与Ref引用问题求助
I’ve run into nearly identical headaches using react-quill with TypeScript in Next.js, so let’s walk through fixing your ref and type problems step by step:
1. Add Proper Type Dependencies
First, make sure you have type support for both react-quill and the underlying Quill editor. While react-quill v2+ includes built-in types, you’ll still need @types/quill for full editor instance type safety:
npm install @types/quill --save-dev # or yarn add @types/quill --dev
2. Fix Dynamic Import Type
When using next/dynamic, the imported component loses type information by default. We’ll explicitly type it using react-quill’s built-in ReactQuillProps:
import type { ReactQuillProps } from 'react-quill'; const ReactQuill = dynamic<ReactQuillProps>(import('react-quill'), { ssr: false, loading: () => <p>Loading ...</p>, });
3. Type the Ref Correctly
Ditch the any type for your ref—instead, type it to match the react-quill component instance, which exposes the getEditor() method that returns a Quill instance:
import Quill from 'quill'; // ... const quillRef = useRef<{ getEditor: () => Quill }>(null);
4. Clean Up Type Annotations in imageHandler
Replace loose any types with proper TypeScript types for the file input and handle null cases safely:
const imageHandler = async () => { const input = document.createElement('input'); input.setAttribute('type', 'file'); input.setAttribute('accept', 'image/*'); input.click(); input.onchange = async () => { const file = input.files?.[0] as File | null; if (!file) return; // Guard clause to avoid null errors const formData = new FormData(); formData.append("file", file); if (quillRef.current) { const quillObj = quillRef.current.getEditor(); // Now you get full type support for Quill methods here // Example: After uploading, insert the image into the editor: // const imageUrl = await yourUploadAPI(formData); // const range = quillObj.getSelection(); // if (range) quillObj.insertEmbed(range.index, 'image', imageUrl); } }; }
Full Modified Code
Here’s the complete fixed version of your component:
import React, { useState, useRef } from 'react'; import dynamic from 'next/dynamic'; import { Container } from "@mui/material"; import type { ReactQuillProps } from 'react-quill'; import Quill from 'quill'; import 'react-quill/dist/quill.snow.css'; const ReactQuill = dynamic<ReactQuillProps>(import('react-quill'), { ssr: false, loading: () => <p>Loading ...</p>, }); const Editor = () => { const [value, setValue] = useState(''); const quillRef = useRef<{ getEditor: () => Quill }>(null); const imageHandler = async () => { const input = document.createElement('input'); input.setAttribute('type', 'file'); input.setAttribute('accept', 'image/*'); input.click(); input.onchange = async () => { const file = input.files?.[0] as File | null; if (!file) return; const formData = new FormData(); formData.append("file", file); if (quillRef.current) { const quillObj = quillRef.current.getEditor(); // Add your file upload logic here, then insert the image into the editor } }; }; const modules = { toolbar: { container: [ [{ font: [] }, { 'size': [] }, { 'header': [1, 2, 3, 4, 5, 6] }], ['bold', 'italic', 'underline', 'strike'], [{ 'color': [] }, { 'background': [] }], [{ 'script': 'sub' }, { 'script': 'super' }], [{ 'header': 1 }, { 'header': 2 }, 'blockquote', 'code-block'], [ { list: 'ordered' }, { list: 'bullet' }, { indent: '-1' }, { indent: '+1' }, ], [{ 'direction': 'rtl' }, { 'align': [] }], ['link', 'image', 'clean'], ], handlers: { image: imageHandler } } }; return ( <Container maxWidth="xxxl" disableGutters> <ReactQuill ref={quillRef} value={value} modules={modules} onChange={setValue} placeholder="Start typing!" /> </Container> ); }; export default Editor;
Key Fixes Explained
- Typed Dynamic Import: Ensures the
ReactQuillcomponent has all correct prop types, so you get autocompletion and type checking for props likemodulesoronChange. - Typed Ref: Eliminates unsafe
anytypes and gives you full type support for the Quill editor instance returned bygetEditor(). - Type-Safe File Handling: Adds a guard clause to handle null file cases and properly types the file object, preventing runtime errors.
内容的提问来源于stack exchange,提问作者Kate




