zenoamaro / react-quill

A Quill component for React.
https://zenoamaro.github.io/react-quill
MIT License
6.69k stars 910 forks source link

Proper Approach to Using an Image Handler #961

Open ryxxn opened 5 months ago

ryxxn commented 5 months ago

Problem

Using document.createElement to implement an image handler in React-Quill can lead to unexpected bugs.

A significant issue is that the upload function fails to update with dynamically changing props or states. Once created, the function does not adapt to reflect updates in the component's props or state.

Example

export default function Editor({ value, onChange, ...other }) {
  const quillRef = useRef(null);

  const imageHandler = () => {
    const input = document.createElement('input');
    input.setAttribute('type', 'file');
    input.setAttribute('accept', 'image/*');
    input.click();

    input.addEventListener('change', () => {
      const files = input.files;
      // image upload function
    });
  };

  const modules = useMemo(() => {
    return {
      toolbar: {
        container: '--',
        handlers: {
          image: imageHandler,
        },
      },
      history: {},
      clipboard: {},
      // custom
    };
  }, []);

  return (
      <ReactQuill
        ref={quillRef}
        theme="snow"
        modules={modules}
        formats={formats}
        value={value}
        onChange={onChange}
        placeholder="input text..."
        {...other}
      />
  );
}

Solution

To address these issues, it's recommended to use React's useRef to reference the file input tag. This approach allows for managing DOM elements within React, thereby preventing synchronization issues between React's virtual DOM and the actual DOM. Here's a simplified example of how this can be implemented.

export default function Editor({ value, onChange, ...other }) {
  const inputRef = useRef(null);

  const quillRef = useRef(null);

  const onChangeImage = () => {
    const files = inputRef.current.files;
    // Image upload function
  };

  const modules = useMemo(() => {
    return {
      toolbar: {
        container: '--',
        handlers: {
          image: () => inputRef.current.click(),
        },
      },
      history: {},
      clipboard: {},
      // Custom modules
    };
  }, []);

  return (
    <div>
      <input
        ref={inputRef}
        className="hide" // hide
        type="file"
        onChange={onChangeImage}
      />
      <ReactQuill
        ref={quillRef}
        theme="snow"
        modules={modules}
        formats={formats}
        value={value}
        onChange={onChange}
        placeholder="input text..."
        {...other}
      />
    </div>
  );
}
alilland commented 4 months ago

any advice on hooking into the editor in order to react to the image in the editor pallet?