Search code examples
reactjsdraftjs

Multiple draftjs components with plugins at once


I've been using draft js with the richbuttons plugin, and everything works fine if I only have one editor component. However, when I try to add more than one component on the page, the rich buttons only apply to the most recently added editor.

I've read about adding multiple plugins, but their included example doesn't account for importing components into the plugins. What I'm trying to do is something like:

const richButtonsPlugin = createRichButtonsPlugin();
const {ItalicButton, BoldButton, UnderlineButton, OLButton, ULButton, H2Button} = richButtonsPlugin;
const plugins = [richButtonsPlugin]

const richButtonsPlugin2 = createRichButtonsPlugin();
const {ItalicButton, BoldButton, UnderlineButton, OLButton, ULButton, H2Button} = richButtonsPlugin2;
const plugins2 = [richButtonsPlugin2]

But if I try to do that, I get a compile error saying

Module build failed: Duplicate declaration "ItalicButton"

I'm planning on having 8+ editors operating at once in my single-page app, so is there any way to initialize rich button plugins for each of those that will be connected to their respective editor components, and to reuse the same button components (i.e. ItalicButton, BoldButton) ?

I'll be honest that I don't quite understand the syntax of this line:

const {ItalicButton, BoldButton, UnderlineButton, OLButton, ULButton, H2Button} = richButtonsPlugin;

So any resources to understand what is happening there would be much appreciated, thank you!


Solution

  • I'll be honest that I don't quite understand the syntax of this line:

    const {ItalicButton, BoldButton, UnderlineButton, OLButton, ULButton, H2Button} = richButtonsPlugin;
    

    This is called destructuring, and is a way to extract data from objects (or arrays), and putting them into variables. You'd get the same result if you wrote:

    const ItalicButton = richButtonsPlugin.ItalicButton
    const BoldButton = richButtonsPlugin.BoldButton
    const UnderlineButton = richButtonsPlugin.UnderlineButton
    ...and so on
    

    And this is why you get the error:

    Module build failed: Duplicate declaration "ItalicButton"
    

    You've created the variable const ItalicButton twice (you've actually done it with all of them).

    What I think you should do is: Turn your editor, with the rich buttons, into its own component. It could look something like this:

    import Editor from 'draft-js-plugins-editor';
    import createRichButtonsPlugin from 'draft-js-richbuttons-plugin';
    
    const richButtonsPlugin = createRichButtonsPlugin();
    
    const { ItalicButton, BoldButton, UnderlineButton, OLButton, ULButton, H2Button } = richButtonsPlugin;
    
    const plugins = [
      richButtonsPlugin 
      //...other plugins
    ];
    
    export default class MyEditor extends Component {
      render() {
        return (
          <div>
            <div className="myToolbar">
              <BoldButton/>
              <ItalicButton/>
              <UnderlineButton/>
              <OLButton/>
              <ULButton/>
              <H2Button/>
            </div>
            <Editor
              editorState={this.props.editorState}
              handleChange={this.props.handleChange}
              plugins={plugins}
              // ...other props
            />
          </div>
        );
      }
    }
    

    Let's say you put this component into a file called my-editor.js. Now you can re-use it from wherever you want, like this:

    import MyEditor from './my-editor.js';
    import { EditorState } from 'draft-js';
    
    export default class App extends Component {
    
      state = {
        editorState: EditorState.createEmpty(),
      };
    
      handleChange = (editorState) => {
        this.setState({ editorState })
      }
    
      render() {
        return (
          <div>
            <MyEditor
              editorState={this.state.editorState}
              handleChange={this.handleChange}
            />
          </div>
        );
      }
    }
    

    If you need multiple instances of them on the same page, you could create an array of editorStates like this:

    export default class App extends Component {
    
      state = {
        editorStates: [EditorState.createEmpty(), EditorState.createEmpty()]
      };
    
      handleChange = (index) => (editorState) => {
        const currentStates = this.state.editorStates
        const updatedStates = currentStates[index] = editorState
        this.setState({ editorStates: updatedStates })
      }
    
      render() {
        return (
          <div>
            {this.state.editorStates.map((editorState, index) =>
              <MyEditor
                editorState={this.state.editorState}
                handleChange={this.handleChange(index)}
              />
            })
          </div>
        );
      }
    }
    

    I haven't tried running this code, but it should point you in the right direction either way. Hope it helps, and just let me know if something is unclear or doesn't work!