Search code examples
reactjsdraftjs

Accessing draftjs output from parent


I have a site where we are using multiple forms on the same page. I have used Draftjs with react to create rich html text inputs, which I have customized to use only the controls I actually need:

class RichEditor extends React.Component {
    constructor(props) {
        super(props);
        this.state = {editorState: EditorState.createEmpty()};

        this.focus = () => this.refs.editor.focus();
        this.onChange = (editorState) => {
            this.setState({editorState});
        }

        this.handleKeyCommand = (command) => this._handleKeyCommand(command);
        this.onTab = (e) => this._onTab(e);
        this.toggleBlockType = (type) => this._toggleBlockType(type);
        this.toggleInlineStyle = (style) => this._toggleInlineStyle(style);
    }

    _handleKeyCommand(command) {
        const {editorState} = this.state;
        const newState = RichUtils.handleKeyCommand(editorState, command);

        if (newState) {
            this.onChange(newState);
            return true;
        }
        return false;
    }

    _onTab(e) {
        const maxDepth = 4;
        this.onChange(RichUtils.onTab(e, this.state.editorState, maxDepth));
    }

    _toggleBlockType(blockType) {
        this.onChange(
            RichUtils.toggleBlockType(
                this.state.editorState,
                blockType
            )
        );
    }

    _toggleInlineStyle(inlineStyle) {
        this.onChange(
            RichUtils.toggleInlineStyle(
                this.state.editorState,
                inlineStyle
            )
        );
    }

    render() {
        const { editorState } = this.state;

        let className = 'RichEditor-editor';
        var contentState = editorState.getCurrentContent();

        if (!contentState.hasText()) {
            if (contentState.getBlockMap().first().getType() !== 'unstyled') {
                className += ' RichEditor-hidePlaceholder';
            }
        }

        return (
            <div className="RichEditor-root">
                <BlockStyleControls
                    editorState={editorState}
                    onToggle={this.toggleBlockType}
                />
                <InlineStyleControls
                    editorState={editorState}
                    onToggle={this.toggleInlineStyle}
                />
                <div className={className} onClick={this.focus}>
                <Editor
                    blockStyleFn={getBlockStyle}
                    customStyleMap={styleMap}
                    editorState={editorState}
                    handleKeyCommand={this.handleKeyCommand}
                    onChange={this.onChange}
                    onTab={this.onTab}
                    placeholder=""
                    ref="editor"
                    spellCheck={true}
                />
                </div>
            </div>
        );
    }
}

module.exports = RichEditor

My parent component looks like this:

class WrapperComponent extends React.Component {

    constructor(props) {
        super(props);
        this.onChange = (editorState2) => {
            this.setState({editorState2});
            console.log("onChange");
            console.log(editorState2);
        }
        this.state = {
            editorState1: EditorState.createEmpty(),
            editorState2: EditorState.createEmpty(),
            html: null
        }
    }

    update() {
        console.log("update");
        console.log(this.state.editorState2);
        console.log(convertToRaw(this.state.editorState2.getCurrentContent()));
        //this.setState({ draftEditor2: e.value });
        this.setState({ html: stateToHTML(this.state.editorState2.getCurrentContent()) });
    }

    render () {

        const Editable = () => (
            <div className="editor">
                <div className="editor-inner">
                    <h3>Redigerar: Anbudsbrev</h3>
                    <h4>Rubrik</h4>
                    <input type="text" name="title" />
                    <h4>Text 1</h4>
                    <RichEditor editorState={this.state.editorState1} updateStateToParent={this.onChange} name="text01" ref="editor" />
                    <h4>Citat</h4>
                    <input type="text" name="quote01" />
                    <h4>Text 2</h4>
                    <RichEditor updateStateToParent={this.onChange} name="text02" ref="editor" />
                    <EditorFooter {...this.props} submitForm={this.submitForm} />
                    <button onClick={this.update.bind(this)}>Update</button>
                    <div>{this.state.html}</div>
                </div>
            </div>
        );

        const Readable = () => (
            <div>
                <h1 className="header66">{this.props.title}</h1>
                <div className="text66">{this.props.text01}</div>
                <div className="quote100">{this.props.quote01}</div>
                <div className="text66">{this.props.text02}</div>
            </div>
        );

        return (
            <div>
                { this.props.isInEditMode ? <Editable /> : <Readable /> }
            </div>
        );
    }
};

WrapperComponent.defaultProps = {
    height: 100,
    title: "Lorem ipsum dolor sit amet",
    text01: "Mauris sollicitudin purus accumsan libero fringilla, vitae vulputate lorem aliquam. Nunc ipsum nisl, consectetur ac ultrices ac, pretium vitae lectus. Cras vestibulum, arcu non condimentum hendrerit, dolor ante molestie ante, eget dapibus felis quam a ante. Nunc fringilla risus eget nunc tincidunt sodales.",
    quote01: "Aliquam erat volutpat.",
    text02: "Praesent non erat quis sem mollis sodales. Integer convallis metus in ligula vehicula, quis vulputate lectus accumsan. Aliquam luctus posuere mollis. Aliquam luctus dignissim quam, ut aliquet ligula pellentesque ac. Nulla sodales lacus sem, eu pharetra arcu aliquet ac. Sed in venenatis libero."
};

WrapperComponent.propTypes = {
    content: PropTypes.object,
    itemId: PropTypes.string
}

module.exports = WrapperComponent

But I seem to be misunderstanding something fundamental. I can't get the value from either of the editors.

If I send updateStateToParent to my RichEditor I can see some stuff happening, but I can't grab the state from the RichEditor and add it to the state of the WrapperComponent.

I think I am approaching this in the wrong way, but I can't get my head around how to solve it.


Solution

  • I think you're pretty close here, but the problem is that you aren't using the parent to set the editorStates. What you should do is storing and updating the editorStates inside your parent component.

    This means that the RichEditor-component must:

    1. Receive its editorState as a prop
    2. Tell its parent about changes inside it, and let the parent update the editorState (which in turn will be passed down again).

    Here's a minimal example:

    class RichEditor extends React.Component {
        render() {
            const { editorState, onChange } = this.props;
    
            return (
              <div style={{ background: 'white' }}>
                <Editor editorState={editorState} onChange={onChange} />
              </div>
            );
        }
    }
    

    The parent in its turn must:

    1. Create the initial editorStates for each RichEditor (and pass them down)
    2. Update those editorsStates each time they change

    Here's a minimal example of that:

    class WrapperComponent extends React.Component {
        constructor(props) {
            super(props);
    
            this.state = {
                editorState1: EditorState.createEmpty(),
                editorState2: EditorState.createEmpty()
            }
        }
    
        onChange = (editorStateKey) => (editorState) => {
            this.setState({ [editorStateKey]: editorState });
        }
    
        render () {
          return (
            <div>
              <h4>Editor 1</h4>
              <RichEditor editorState={this.state.editorState1} onChange={this.onChange('editorState1')} />
              <h4>Editor 2</h4>
              <RichEditor editorState={this.state.editorState2} onChange={this.onChange('editorState2')} />
            </div>
          )
        }  
    };
    

    And finally, here's a working fiddle: https://jsfiddle.net/bwk32xuo/