React Quill disappears on rerender

228 views Asked by At

I'm working on creating a text editor that handle image uploads, everything works fine but when it re renders it completely disappears

I tried using useMemo on modules and also add a unqiue key to but nothing seemed to work. if im removing the image handler from my modules then it stops disappearing.

Can anyone please guide, what the error is?

const uploadToCloudinary = async (file: File): Promise<string> => {
  //code for uploading
  return url;
};

export interface EditorProps {
  description: string;
  setDescription: (description: string) => void;
}

export default function QuillEditor({
  description,
  setDescription,
}: EditorProps) {
  const [value, setValue] = useState<string>(description || "");
  const [qid, setId] = useState<string>("");
  const reactQuillRef = useRef<ReactQuill>(null);

  const onChange = (content: string) => {
    setValue(content);
    console.log(content);
    setDescription(content);
  };

  const imageHandler = useCallback(() => {
    const input = document.createElement("input");
    input.setAttribute("type", "file");
    input.setAttribute("accept", "image/*");
    input.click();
    input.onchange = async () => {
      if (input !== null && input.files !== null) {
        const file = input.files[0];
        const url = await uploadToCloudinary(file);
        const quill = reactQuillRef.current;
        if (quill) {
          const range = quill.getEditorSelection();
          range && quill.getEditor().insertEmbed(range.index, "image", url);
        }
      }
    };
  }, []);
  const modules = {
    toolbar: {
      container: [
        [{ header: [1, 2, 3, 4, 5, 6, false] }],
        ["bold", "italic", "underline", "strike", "blockquote"],
        [{ list: "ordered" }, { list: "bullet" }],
        ["link", "image"],
        ["code-block"],
        [
          {
            color: [
              "#000000", ...
            ],
          },
          {
            background: [
              "#000000", ...
            ],
          },
        ],
        ["clean"],
      ],
      handlers: {
        image: imageHandler,
      },
    },
    clipboard: {
      matchVisual: false,
    },
  };

  return (
    <ReactQuill
      ref={reactQuillRef}
      theme="snow"
      placeholder="Start writing..."
      modules={modules}
      formats={[
        "header", ...
      ]}
      value={value}
      onChange={onChange}
      style={{ height: "350px", overflowY: "auto" }}
    />
  );
}

3

There are 3 answers

1
reda baha On

The modules object is recreated/re-defined whenever the component rerenders. Just memoize the object with useMemo:

  const modules = useMemo(() => (
//your object here
), [imageHandler]);
0
Kaloyan On

I have almost the same implementation with the only difference being that my imageHandler function does not use useCallback and is instead just an async function. I haven't encountered this problem so I think you should try that.

0
Adrian On

I had the same problem with the rerender, but I extracted modules to a variable and used useMemo on it, but the dependency array I left empty because the problem kept coming back, now everything works as I expect.

 const modules = useMemo(
        () => ({
            toolbar: {
                container: [
                    ['bold', 'italic', 'underline', 'strike'],
                    [{ header: 1 }, { header: 2 }],
                    [{ list: 'ordered' }, { list: 'bullet' }],
                    [{ script: 'sub' }, { script: 'super' }],
                    [{ direction: 'rtl' }],
                    [{ header: [1, 2, 3, 4, 5, 6, false] }],
                    [{ color: [] }, { background: [] }],
                    [{ font: [] }],
                    [
                        {
                            size: fontSizeArray,
                        },
                    ],
                    [{ align: [] }],
                    ['link', 'image'],
                    ['loacalImage'],
                    ['socialLink'],
                ],
                handlers: {
                    loacalImage: openElfinder,
                    socialLink: handleOpenModal,
                },
            },
        }),
        // eslint-disable-next-line react-hooks/exhaustive-deps
        []
    );