I am working on a homepage that consists of eight different components representing various aspects of the page content. I want to have three of them be editable using a custom built InlineEditor
component that uses Draft.js. When I tried to do this initially, the editing toolbars for the second and third component would only work on the first component. So this is a Draft.js issue. I was able to get everything to work by creating three separate InlineEditor
components and then using each one individually when necessary, but that seems redundant.
I'm thinking there has to be a better way to do this using HOCs or render props
but I'm struggling to set that up properly. Any ideas on what might be a better way to go about this?
Here's the initial InlineEditor
component:
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)
Currently this is being accessed by components such as About
like so:
return (
<InlineEditor
auth={ this.props.auth }
rawContent={ about }/>
)
I was able to solve this problem by creating one InlineEditor
component that passed in the toolbar plugins inside of its constructor
, i.e.
constructor(props) {
super(props)
this.undoPlugin = createUndoPlugin()
this.toolbarLinkPlugin = createToolbarLinkPlugin({
inputPlaceholder: "Insert URL here...",
})
// etc...
}