react-draft-wysiwyg: getting a setState error when using MaterialUI tabs

This looks like an error that was introduced with version 1.14.3. If the WYSIWYG Editor is in a MaterialUI tab, when you switch away from the editor, you get this:

Warning: Can't call setState on a component that is not yet mounted. This is a no-op, but it might indicate a bug in your application. Instead, assign to `this.state` directly or define a `state = {};` class property with the desired state in the r component.

Here’s an isolated case that produces the issue (at least in my environment):

import React, { Fragment, useState } from 'react';

import Tabs from '@material-ui/core/Tabs';
import Tab from '@material-ui/core/Tab';

import { EditorState, ContentState } from 'draft-js';
import { Editor } from 'react-draft-wysiwyg';
import htmlToDraft from 'html-to-draftjs';

const toDraft = (value: any) => {
  const draftValue = htmlToDraft(value || '');
  const { contentBlocks, entityMap } = draftValue;
  const contentState = ContentState.createFromBlockArray(
    contentBlocks,
    entityMap
  );

  return EditorState.createWithContent(contentState);
};

const Test = () => {
  const [tabIndex, setTabIndex] = useState(0);
  const [editorState, setEditorState] = useState(toDraft('Hello, world!'));

  return (
    <Fragment>
      <Tabs
        centered={true}
        indicatorColor="primary"
        onChange={(evt: any, value: number) => {
          setTabIndex(value);
        }}
        textColor="primary"
        value={tabIndex}
      >
        <Tab label="Editor" value={0} />
        <Tab label="One" value={1} />
      </Tabs>
      <div>
        {tabIndex === 0 ? (
          <div>
            <Editor
              editorState={editorState}
              onEditorStateChange={(value: any) => {
                setEditorState(value);
              }}
              toolbar={{
                inline: {
                  options: ['bold', 'italic', 'underline', 'strikethrough']
                },
                list: {
                  options: ['unordered', 'ordered']
                },
                options: ['inline', 'blockType', 'list', 'textAlign'],
                textAlign: {
                  options: ['left', 'center', 'right']
                }
              }}
            />
          </div>
        ) : null}
        {tabIndex === 1 ? <div>One</div> : null}
      </div>
    </Fragment>
  );
};

export default Test;

About this issue

  • Original URL
  • State: open
  • Created 4 years ago
  • Reactions: 2
  • Comments: 16 (2 by maintainers)

Most upvoted comments

@jpuri Are you able to take a look at some of the PR which might fix this please? (#1044 & #1008) There are now a few issues around which I think relate to the same problem (#953, #951 & #918). I’d like to use this for an application but this issue is currently preventing me (and I’m sure others too). Thank you 😄

Guys, as it looks like this repo is not maintained I’ve re-forked and pushed updated version (based on https://github.com/jpuri/react-draft-wysiwyg/pull/1044): https://www.npmjs.com/package/@nick4fake/react-draft-wysiwyg

Please note, that for typescript you will need to manually add definitions:

declare module '@nick4fake/react-draft-wysiwyg' {
  import * as React from 'react';
  import * as Draft from 'draft-js';

  export type SyntheticKeyboardEvent = React.KeyboardEvent<{}>;
  export type SyntheticEvent = React.SyntheticEvent<{}>;
  export type RawDraftContentState = Draft.RawDraftContentState;

  export class EditorState extends Draft.EditorState {}
  export class ContentState extends Draft.ContentState {}
  export class ContentBlock extends Draft.ContentBlock {}
  export class SelectionState extends Draft.SelectionState {}

  export interface EditorProps {
    webDriverTestID?: string;
    onChange?(contentState: RawDraftContentState): void;
    onEditorStateChange?(editorState: EditorState): void;
    onContentStateChange?(contentState: RawDraftContentState): void;
    initialContentState?: RawDraftContentState;
    defaultContentState?: RawDraftContentState;
    contentState?: RawDraftContentState;
    editorState?: EditorState;
    defaultEditorState?: EditorState;
    toolbarOnFocus?: boolean;
    spellCheck?: boolean;
    stripPastedStyles?: boolean;
    toolbar?: object;
    toolbarCustomButtons?: Array<React.ReactElement<HTMLElement>>;
    toolbarClassName?: string;
    toolbarHidden?: boolean;
    locale?: string;
    localization?: object;
    editorClassName?: string;
    wrapperClassName?: string;
    toolbarStyle?: object;
    editorStyle?: React.CSSProperties;
    wrapperStyle?: React.CSSProperties;
    uploadCallback?(file: object): Promise<object>;
    onFocus?(event: SyntheticEvent): void;
    onBlur?(event: SyntheticEvent): void;
    onTab?(event: SyntheticKeyboardEvent): void;
    mention?: object;
    hashtag?: object;
    textAlignment?: string;
    readOnly?: boolean;
    tabIndex?: number;
    placeholder?: string;
    ariaLabel?: string;
    ariaOwneeID?: string;
    ariaActiveDescendantID?: string;
    ariaAutoComplete?: string;
    ariaDescribedBy?: string;
    ariaExpanded?: string;
    ariaHasPopup?: string;
    customBlockRenderFunc?(block: ContentBlock): any;
    wrapperId?: number;
    customDecorators?: object[];
    editorRef?(ref: object): void;
    handlePastedText?(
      text: string,
      html: string,
      editorState: EditorState,
      onChange: (editorState: EditorState) => void,
    ): boolean;
    customStyleMap?: object;
  }

  export class Editor extends React.Component<EditorProps> {
    constructor(props: Readonly<EditorProps>);
    focusEditor(): void;
  }

}

@oikantik to be clear, it doesn’t actually go away on production–React just doesn’t report errors in production environments, for performance and security reasons.