在单个页面中使用具有多个 Draft.js 编辑器的高阶组件

Using a higher order component with multiple Draft.js editors in single page

我正在制作一个主页,该主页由代表页面内容各个方面的八个不同组件组成。我想让其中三个可以使用使用 Draft.js 的自定义构建 InlineEditor 组件进行编辑。当我最初尝试这样做时,第二个和第三个组件的编辑工具栏仅适用于第一个组件。所以这是一个 Draft.js 问题。通过创建三个单独的 InlineEditor 组件然后在必要时单独使用每个组件,我能够让一切正常工作,但这似乎是多余的。

我认为必须有更好的方法来使用 HOC 或 render props 来执行此操作,但我正在努力正确设置它。关于什么可能是解决此问题的更好方法的任何想法?

这是初始 InlineEditor 组件:

import React, { Component } from 'react'
import { connect } from 'react-redux'
import { EditorState, convertFromRaw } from 'draft-js'
import Editor from 'draft-js-plugins-editor'
import createUndoPlugin from 'draft-js-undo-plugin'
import createToolbarPlugin, { Separator } from 'draft-js-static-toolbar-plugin'
import createToolbarLinkPlugin from 'draft-js-toolbar-link-plugin'
import {
  ItalicButton,
  BoldButton,
  UnderlineButton,
  CodeButton,
  HeadlineOneButton,
  HeadlineTwoButton,
  HeadlineThreeButton,
  UnorderedListButton,
  OrderedListButton,
  BlockquoteButton,
  CodeBlockButton
} from 'draft-js-buttons'
import { Flex, Box } from 'rebass'
import FontAwesome from 'react-fontawesome'
import {
  ToolbarNav,
  EditPanel,
  StaticToolbar,
  TextInfo,
  DefaultButton,
  SuccessButton,
  InlineLink
} from 'styles'

// Set up Draft.js toolbar and plugins
const undoPlugin = createUndoPlugin()
const toolbarLinkPlugin = createToolbarLinkPlugin({
  inputPlaceholder: 'Insert URL here...'
})
const { LinkButton } = toolbarLinkPlugin
const { UndoButton, RedoButton } = undoPlugin
const toolbarPlugin = createToolbarPlugin({
  structure: [
    BoldButton,
    ItalicButton,
    UnderlineButton,
    CodeButton,
    Separator,
    HeadlineOneButton,
    HeadlineTwoButton,
    HeadlineThreeButton,
    Separator,
    UnorderedListButton,
    OrderedListButton,
    BlockquoteButton,
    CodeBlockButton,
    LinkButton,
    Separator,
    UndoButton,
    RedoButton
  ]
})
const { Toolbar } = toolbarPlugin
const plugins = [toolbarPlugin, toolbarLinkPlugin, undoPlugin]

class InlineEditor extends Component {
  displayName = 'inline editor component'
  constructor(props) {
    super(props)

    this.state = {
      editorState: EditorState.createWithContent(
        convertFromRaw(this.props.rawContent)
      ),
      showURLInput: false,
      urlValue: '',
      readOnly: true
    }
  }
  onChange = editorState => this.setState({ editorState })
  focus = () => this.refs.editor.focus()
  onEdit = e => {
    e.preventDefault()
    this.setState({
      readOnly: false
    })
  }
  onSave = () => {
    // save new content
    this.setState({
      showURLInput: false,
      urlValue: '',
      readOnly: true
    })
  }
  onCancel = () => {
    // cancel editing
    this.setState({
      editorState: EditorState.createWithContent(
        convertFromRaw(this.props.rawContent),
        this.decorator
      ),
      showURLInput: false,
      urlValue: '',
      readOnly: true
    })
  }
  renderToolbar = () => {
    return (
      <ToolbarNav>
        <StaticToolbar>
          <Toolbar />
        </StaticToolbar>
      </ToolbarNav>
    )
  }
  render() {
    const { editorState, readOnly } = this.state
    const { auth } = this.props
    return (
      <EditPanel>
        <Flex wrap>
          <Box w={'90%'}>{!readOnly && this.renderToolbar()}</Box>
          <Box mt={1}>
            <Editor
              editorState={editorState}
              onChange={this.onChange}
              plugins={plugins}
              ref="{(element) => { this.editor = element }}"
              readOnly={readOnly}
            />
            {auth.isAuthenticated &&
              readOnly && (
                <TextInfo>
                  <InlineLink onClick={this.onEdit} title="Edit">
                    <FontAwesome name="pencil" /> Edit
                  </InlineLink>
                </TextInfo>
              )}
          </Box>
          <Box width={'40%'} mr={1} mt={1}>
            {!readOnly && (
              <DefaultButton
                className={`block`}
                type="button"
                onClick={this.onCancel}>
                Cancel
              </DefaultButton>
            )}
          </Box>
          <Box width={'40%'} mt={1}>
            {!readOnly && (
              <SuccessButton
                className={`block`}
                type="button"
                onClick={this.onSave}>
                Save
              </SuccessButton>
            )}
          </Box>
        </Flex>
      </EditPanel>
    )
  }
}

const mapStateToProps = state => {
  return {
    auth: state.auth
  }
}

export default connect(mapStateToProps)(InlineEditor)

目前,About 等组件正在访问它,如下所示:

return (
    <InlineEditor
    auth={ this.props.auth }
    rawContent={ about }/>
)

我能够通过创建一个 InlineEditor 组件来解决这个问题,该组件在其 constructor 内部的工具栏插件中传递,即

constructor(props) {
    super(props)

    this.undoPlugin = createUndoPlugin()

    this.toolbarLinkPlugin = createToolbarLinkPlugin({
       inputPlaceholder: "Insert URL here...",
    })

    // etc...
}