Reputation: 227
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
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.
react-quill-new
library. ( Must )Upvotes: 0
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
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
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
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