state doesn't update when linking to a url from toolbar in react-draft-wysiwyg

341 Views Asked by At

I am using react-draft-wysiwyg in my project. It works fine most cases. Only problem is, when I try to link a text to url. In that case it doesn't update the state.

Let's say, I already have a text "Hello world!". Now if I add more text in this e.g. "Hello world! newText". I want to link this "newText" to url using link option from toolbar and click save. It doesn't update the state with the link and goes back previous text "Hello world!" Interesting thing is that after adding a link if I add more text to that, it works just fine.

const createStateFromHtml = (html: string) => {
    const blocksFromHtml = htmlToDraft(html)
    const { contentBlocks, entityMap } = blocksFromHtml
    const contentState = ContentState.createFromBlockArray(contentBlocks, entityMap)

    return EditorState.createWithContent(contentState)
}

const createHtmlFromState = (editorState: EditorState) => {
    const rawContentState = convertToRaw(editorState.getCurrentContent())

    return draftToHtml(rawContentState)
}

const htmlSanitizeSettings = {
    ADD_TAGS: ['iframe'],
    ADD_ATTR: ['allowfullscreen'],
}

const BodyEditor = (props: BodyEditorProps) => {
    DOMPurify.setConfig(htmlSanitizeSettings)

    const initialState = props.html && props.html !== '' ? createStateFromHtml(props.html) : EditorState.createEmpty()
    const [editorState, setEditorState] = React.useState(initialState)

    const setEditorHtml = () => {
        const html = createHtmlFromState(editorState)
        const htmlWithIframeSettings = addPropertiesToIframes(html)
        const purifiedHtml = DOMPurify.sanitize(htmlWithIframeSettings)
        props.setBody(purifiedHtml)
    }

    /*
     * Adds a div element around iframes and adds class="embedded-video" around so we can use CSS to make iframes reponsive
     */
    const addPropertiesToIframes = (html: string) => {
        return (
            html
                // Let's assume embedded iframes are videos
                .replace(/<iframe/g, '<div class="iframe-container"><iframe class="embedded-video"')
                .replace(/<\/iframe>/g, '</iframe></div>')
                .replace(/iframe width=/g, 'iframe allowfullscreen width=')
                // Let's remove embedded-video class from embedded spotify iframes
                // This should be done for all non-video embeds because otherwise the iframe are made to have 16:9 aspect ratio
                .replace(
                    // eslint-disable-next-line no-useless-escape
                    /class=\"embedded-video\" src=\"https:\/\/open\.spotify\.com/g,
                    'src="https://open.spotify.com',
                )
        )
    }

    return props.editable ? (
        <Editor
            editorStyle={editorStyle}
            editorState={editorState}
            onEditorStateChange={setEditorState}
            onBlur={setEditorHtml}
            toolbar={{
                options: toolbarOptions,
                image: {
                    ...imageOptions,
                    uploadCallback: props.imageUploadCallback,
                },
                link: {
                    ...linkOptions,
                },
                embedded: {
                    ...embeddedOptions,
                },
            }}
            placeholder={props.placeholder}
        />
    ) : (
        <div
            onClick={() => props.toggleEditable(props.name)}
            dangerouslySetInnerHTML={
                props.html
                    ? { __html: DOMPurify.sanitize(props.html!) }
                    : { __html: DOMPurify.sanitize(props.placeholder!) }
            }
        />
    )
}

export default BodyEditor

Any help would be highly appreciated. I am stuck on this for a very long time.

1

There are 1 best solutions below

1
On

I used this combine. It is working properly.

import { Editor } from "react-draft-wysiwyg";
import { useEffect } from "react";
import {convertToRaw } from 'draft-js';
import draftToHtml from 'draftjs-to-html';

Maybe, you can try that.

For Example:

You can use to the state initial value below:

EditorState.createWithContent(
        ContentState.createFromBlockArray(
          convertFromHTML(<your_data>)
        )
      )

You can use to store the HTML data below:

 draftToHtml(convertToRaw(<your_state>.getCurrentContent()))