fluorspar
fluorspar

Reputation: 227

React-Quill - addRange(): The given range isn't in document

I am trying to make a simple blog using React-Quill text editor. I want to get the value of the ReactQuill Editor and preview it on a different route. I've been trying to achieve this since quite a while now and each time I face the same issue, the quill editor loses focus after every key press and throws a warning in the console saying addRange(): The given range isn't in document.

I've observed one thing. When I just pass in the props to < CreateBlog /> without applying the route, it works perfectly fine. But as soon as I apply the routes to < CreateBlog /> component so that I can take the value from the editor and preview it on a different route, I start facing this issue. I think that the react-router is probably responsible for such behavior but I'm unable to figure out the exact reason and its fix. Please help me. I've attached my code below for reference.

App.js:

class App extends Component {

  constructor(){
    super()

    this.state = {
      title: '',
      text: ''
    }

    this.handleBlogTextChange = this.handleBlogTextChange.bind(this)
    this.handleBlogTitleChange = this.handleBlogTitleChange.bind(this)
  }

  handleBlogTextChange(value){
    this.setState({
      text: value
    })
    console.log(this.state.text)
  }

  handleBlogTitleChange(value){
    this.setState({
      title: value
    })
    console.log(this.state.title)
  }

  render(){
    return (
      <BrowserRouter>
        <div class="App">
          <Switch>
            <Route path='/createBlog' component={() => <CreateBlog text={this.state.text} handleBlogChange={this.handleBlogTextChange} /> } />
            <Route exact path='/preview' component={() => <PreviewBlog title={this.state.title} text={this.state.text} />} />
            <Redirect to='/createBlog' />
          </Switch>
        </div>
      </BrowserRouter>
    );
  }
}

export default App;

CreateBlog.js:

export default class CreateBlog extends Component {

    render() {
        return (
            <>
                   ----------
                        <TextEditor text={this.props.text} handleBlogChange={this.props.handleBlogChange} />
                   ----------
            </>
        )
    }
}

TextEditor.js:

class TextEditor extends Component {

    componentDidMount(){
        const input = document.querySelector('input[data-link]')
        input.dataset.link = 'https://google.co.in'
    }

    modules={
        toolbar: [
            [{ 'header': '1'}, {'header': '2'}, { 'font': [] }],
            [{size: []}],
            ['bold', 'italic', 'underline', 'blockquote','code-block'],
            [{'list': 'ordered'}, {'list': 'bullet'}, 
             {'indent': '-1'}, {'indent': '+1'},{'align': []}],
            ['link', 'image', 'video'],
            ['clean']
          ],
    }
    
    render() {
        return (
            <div className="editor">
                <ReactQuill theme="snow" placeholder="Enter story..." modules={this.modules} value={this.props.text} onChange={this.props.handleBlogChange} />
            </div>
        );
    }
}

Upvotes: 10

Views: 10964

Answers (5)

Jay Maniya
Jay Maniya

Reputation: 1

Worked for me in next js.


"use client";

import React, { Suspense, useMemo, useState } from "react";
import dynamic from "next/dynamic";
const ReactQuill = dynamic(() => import("react-quill-new"), { ssr: false });

import "react-quill-new/dist/quill.snow.css";

const QuillTest = () => {
  const [value, setValue] = useState("");

  const modules = useMemo(
    () => ({
      toolbar: {
        container: [
          [{ header: [1, 2, 3, 4, 5, 6] }, { font: [] }, { size: [] }],
          ["bold", "italic", "underline", "strike", "blockquote"],
          ["color", "background"],
          [{ direction: "rtl" }, { direction: "ltr" }, "align"],
          [
            { list: "ordered" },
            { list: "bullet" },
            { indent: "-1" },
            { indent: "+1" },
          ],
          ["link", "image", "video"],
          ["code-block", "table"],
          ["clean"],
        ],
        handlers: {
          image: () => {
            console.log("image clicked");
          },
        },
      },
    }),
    []
  );

  const formats = useMemo(
    () => [
      "font",
      "size",
      "header",
      "bold",
      "italic",
      "underline",
      "strike",
      "blockquote",
      "list",
      "bullet",
      "indent",
      "link",
      "image",
      "video",
      "code-block",
      "table",
      "direction",
      "align",
      "color",
      "background",
    ],
    []
  );

  return (
    <Suspense fallback={<div>Loading...</div>}>
      <ReactQuill
        key={"quill-editor"}
        theme="snow"
        value={value}
        onChange={setValue}
        placeholder="Compose an epic..."
        modules={modules}
        formats={formats}
        style={{ height: "300px" }}
      />
    </Suspense>
  );
};

export default QuillTest;

Key points to consider.

  1. use useMemo hook.
  2. give a unique key to the component.
  3. use Suspense and dynamic ( for next.js only).
  4. use react-quill-new library. ( Must )

Upvotes: 0

cristian choque
cristian choque

Reputation: 1

If the same thing happens to someone, with the useMemo hook it worked for me

const [value, setValue] = useState("");
    
    const handleMemo = useMemo(
        (content, delta, source, editor) => setValue(content),
        [value]
    );

Upvotes: 0

BROGRAMMER_01
BROGRAMMER_01

Reputation: 49

I was able to solve it by updating the value of value to editor.getContents() inside the onChange callback, which is called back with onChange(content, delta, source, editor), also as mentioned here, in the onChange section we need to use editor specifically to get the new delta not the delta itself, reason explicitly mentioned too.

thus interms of code, what ended up working was :

...
 editing (content, delta, source, editor) {
          const newDelta = editor.getContents ();

          this.setState({value : newDelta, newDelta});
      }

render () {
...
return (
...
<ReactQuill
  theme       = {this.state.theme}
  readOnly    = {readOnly}
  value       = {value}
  placeholder = {'Loading notes...'}
  onChange    = {(content, delta, source, editor) => {this.editing (content, delta, source, editor);}}
  />
...
);
}

Upvotes: 0

Caspian Chen
Caspian Chen

Reputation: 1

use literal object as modules props, it will trigger component rerender , use useMemo hook to memorize modules prop to fix it.

Upvotes: 0

Someindra Singh
Someindra Singh

Reputation: 55

If someone is still searching for an answer, here is how I fixed it

I removed modules and format props and it worked

Upvotes: 1

Related Questions