Ashraf Slamang
Ashraf Slamang

Reputation: 1013

Draft.js integration with Material UI throws missing ref error

I'm having issues integrating Draft.js with Material UI's TextField component (or Input). I followed the documentation (https://material-ui.com/components/text-fields/#integration-with-3rd-party-input-libraries) but it still fails. I've integrated another package using the same method before which worked but there are issues with integrating Draft.js.

Code below:

import React, { useState, useRef, forwardRef } from "react";
import Editor, { createEditorStateWithText } from "draft-js-plugins-editor";
import createEmojiPlugin from "draft-js-emoji-plugin";
import { TextField, makeStyles } from "@material-ui/core";
import { isObject } from "lodash";
import { findDOMNode } from "react-dom";
import classNames from 'classnames';

function InputWrapper(props) {
    const { component: Component, inputRef, ...other } = props;
    const ref = useRef();

    React.useImperativeHandle(inputRef, () => ({
        focus: () => {
            findDOMNode(inputRef.current).focus();
        },
    }));

    return <Component ref={ref} {...other} />;
}

const emojiPlugin = createEmojiPlugin();
const { EmojiSuggestions, EmojiSelect } = emojiPlugin;
const plugins = [emojiPlugin];
const text = `Cool, we can have all sorts of Emojis here. šŸ™Œ
šŸŒæā˜ƒļøšŸŽ‰šŸ™ˆ aaaand maybe a few more here šŸ²ā˜€ļøšŸ—» Quite fun!`;

const useStyles = makeStyles((theme) => ({
    textField: {
        width: "100%",
        minHeight: 31,
        tabSize: 8,
        fontVariantLigatures: "none",
        boxSizing: "content-box",
        userSelect: "text",
        whiteSpace: "pre-wrap",
    },
}));

let DraftInput = (props, ref) => {
    const {
        InputProps,
        InputLabelProps,
        value,
        handleEnterKeyPress,
        onChange,
        id,
        ...other
    } = props;
    const [editorState, setEditorState] = useState(
        createEditorStateWithText(text)
    );
    const baseClasses = useStyles();

    const handleChange = (state) => {
        setEditorState(state);
        // onChange(state);
    };

    const propClass =
        isObject(InputProps) && InputProps.hasOwnProperty("className")
            ? InputProps.className
            : false;

    const classes = classNames([baseClasses, propClass, "emoji-input"]);

    return (
        <div>
                <TextField
                    inputRef={ref}
                    autoFocus={true}
                    value={value}
                    InputProps={{
                        inputComponent: InputWrapper,
                        inputProps: {
                            component: Editor,
                            onChange: handleChange,
                            editorState,
                        },
                        ...InputProps,
                    }}
                    InputLabelProps={{
                        ...InputLabelProps,
                    }}
                    {...InputProps}
                    {...other}
                />
        </div>
    );
};

DraftInput = forwardRef(DraftInput);
export default DraftInput;

Note: there's still test data being used.

Upvotes: 1

Views: 1895

Answers (1)

mhlavacka
mhlavacka

Reputation: 701

This is how I got it to work.

import Editor from "@draft-js-plugins/editor";

const DraftField = React.forwardRef(function DraftField(props, ref) {
  const { component: Component, editorRef, handleOnChange, ...rest } = props;

  React.useImperativeHandle(ref, () => ({
    focus: () => {
      editorRef?.current?.focus();
    },
  }));

  return <Component {...rest} ref={editorRef} onChange={handleOnChange} />;
});

const DraftEditor = () => {
  const [editorState, setEditorState] = React.useState();
  const editorRef = React.useRef(null);

  const handleOnChange = (newEditorState) => {
    setEditorState(newEditorState);
  };

  return (
      <TextField
        fullWidth
        label="Content"
        InputProps={{
          inputProps: {
            component: Editor,
            editorRef,
            editorState,
            handleOnChange
          },
          inputComponent: DraftField,
        }}
      />
  );
};

Upvotes: 1

Related Questions