sstur / react-rte

Pure React rich text WYSIWYG editor based on draft-js.
https://react-rte.org
ISC License
2.86k stars 430 forks source link

How to use RTE in controlled way? #403

Open mogadanez opened 3 years ago

mogadanez commented 3 years ago

Trying to use RTE with "controlled" value, but since setValue(RichTextEditor.createValueFromString(value || "", 'html')) happens on any value change - when I start typing cursor jumping to start of editor

I might not have value when the component initialized, so need a way to update it when value appears,

import React, {Fragment, useState, useEffect} from 'react';
import RichTextEditor from 'react-rte';

const component = ( ( {value,  onChange, error, ...rest } )=>{

    const [value, setValue] = useState(RichTextEditor.createValueFromString(value || "", 'html') || RichTextEditor.createEmptyValue())

    useEffect(() => {
        setValue(RichTextEditor.createValueFromString(value || "", 'html'))
    }, [value])

    const toolbarConfig = {
    ......
    };

    const onChange = (value) => {
        setValue(value)
        if (fieldOnChange) {
            // Send the changes up to the parent component as an HTML string.
            // This is here to demonstrate using `.toString()` but in a real app it
            // would be better to avoid generating a string on each change.
            let html = value.toString('html')
            fieldOnChange(html)
        }
    };

    return   <Fragment>
        <RichTextEditor
            value={value}
            id={field.id}
            onChange={onChange}
            toolbarConfig={toolbarConfig}
            // if there were any more properties specified when calling component htmlField and wanna use them here, we don't have to specify them here one by one
            {...rest}
        />
        <div className="error">{error}</div>
    </Fragment>
})
naishe commented 3 years ago

I am also facing this issue. I want to use it with react-hook-form and it is not letting me. The cursor jump is annoying.

callmeberzerker commented 3 years ago

This is general issue when using draft.js - I would suggest when the RteComponent is mounted -> create editorstate from the initial value (and store it in a ref) -> for any other updates coming to value top -> down ignore the value since draft.js will always have the current state -> your onChange will act like a passive listener to changes in the rte and propagate the changes to your form state mgmt lib.

That's how i've done it in the past, and the only sane way to integrate draftjs text editors with form libs.

Hopefully that was helpful.

the-serious-programmer commented 3 years ago

We recently migrated from Formik 1 to react-hook-form. As @naishe mentioned, she had trouble integrating react-rte into react-hook-form and so did I. Based on @callmeberzerker directions I was able to make the following code work:

export interface ManagedRichTextEditorProps {
  initialValue?: string;
  placeholder?: string;
  onChange: (value: string) => void;
}

export function ManagedRichTextEditor({
  initialValue,
  onChange,
  placeholder,
}: ManagedRichTextEditorProps) {
  const [editorState, setEditorState] = useState(
    initialValue
      ? ReactRTE.createValueFromString(initialValue, 'html')
      : ReactRTE.createEmptyValue(),
  );

  return (
    <RichTextEditor
      value={editorState}
      onChange={value => {
        setEditorState(value);
        onChange(value.toString('html'));
      }}
      placeholder={placeholder}
    />
  );
}

Usage:

<Controller
  control={control}
  name="learning"
  render={({ field }) => (
    <ManagedRichTextEditor
       onChange={data => field.onChange(data)}
       initialValue={learning}
       placeholder="Write what you have learned in this experiment run..."
    />
  )}
/>

What is very important here is the initialValue. Do not use field.value. learning in above example comes from a separate react hooks context.

Even though this library seems pretty dead: @sstur it would be great if this issue gets a follow up in some way (more examples or better form support even though the problem lies in draft.js probably) in the v2 road map issue https://github.com/sstur/react-rte/issues/371