我有一个文本编辑器组件:
import React,{Component} from 'react'
import {Editor, EditorState,RichUtils,convertToRaw} from 'draft-js';
class TextEditor extends Component {
constructor(props) {
super(props);
this.state = { editorState: EditorState.createEmpty() };
this.focus = () => this.refs.editor.focus();
this.onChange = (editorState) => {
this.setState({editorState});
const content = editorState.getCurrentContent();
const editorValue = JSON.stringify(convertToRaw(content));
this.props.update(editorValue);
}
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;
// If the user changes block type before entering any text, we can
// either style the placeholder or hide it. Let's just hide it now.
let className = 'RichEditor-editor';
var contentState = editorState.getCurrentContent();
if (!contentState.hasText()) {
if (contentState.getBlockMap().first().getType() !== 'unstyled') {
className += ' RichEditor-hidePlaceholder';
}
}
return (
<div>
<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="Project Description"
ref="editor"
spellCheck={true}
/>
</div>
</div>
</div>
);
}
}
// Custom overrides for "code" style.
const styleMap = {
CODE: {
backgroundColor: 'rgba(0, 0, 0, 0.05)',
fontFamily: '"Inconsolata", "Menlo", "Consolas", monospace',
fontSize: 16,
padding: 2
}
};
function getBlockStyle(block) {
switch (block.getType()) {
case 'blockquote':
return 'RichEditor-blockquote';
default:
return null;
}
}
class StyleButton extends React.Component {
constructor() {
super();
this.onToggle = (e) => {
e.preventDefault();
this.props.onToggle(this.props.style);
};
}
render() {
let className = 'RichEditor-styleButton';
if (this.props.active) {
className += ' RichEditor-activeButton';
}
return (
<span className={className} onMouseDown={this.onToggle}>
{this.props.label}
</span>
);
}
}
const BLOCK_TYPES = [
{
label: 'H1',
style: 'header-one'
}, {
label: 'H2',
style: 'header-two'
}, {
label: 'H3',
style: 'header-three'
}, {
label: 'H4',
style: 'header-four'
}, {
label: 'H5',
style: 'header-five'
}, {
label: 'H6',
style: 'header-six'
}, {
label: 'Blockquote',
style: 'blockquote'
}, {
label: 'UL',
style: 'unordered-list-item'
}, {
label: 'OL',
style: 'ordered-list-item'
}, {
label: 'Code Block',
style: 'code-block'
}
];
const BlockStyleControls = (props) => {
const {editorState} = props;
const selection = editorState.getSelection();
const blockType = editorState.getCurrentContent().getBlockForKey(selection.getStartKey()).getType();
return (
<div className="RichEditor-controls">
{BLOCK_TYPES.map(
(type) => <StyleButton
key={type.label}
active={type.style === blockType}
label={type.label}
onToggle={props.onToggle}
style={type.style}
/>
)}
</div>
);
};
const INLINE_STYLES = [
{
label: 'Bold',
style: 'BOLD'
}, {
label: 'Italic',
style: 'ITALIC'
}, {
label: 'Underline',
style: 'UNDERLINE'
}, {
label: 'Monospace',
style: 'CODE'
}
];
const InlineStyleControls = (props) => {
var currentStyle = props.editorState.getCurrentInlineStyle();
return (
<div className="RichEditor-controls">
{INLINE_STYLES.map(
type => <StyleButton
key={type.label}
active={currentStyle.has(type.style)}
label={type.label}
onToggle={props.onToggle}
style={type.style}
/>
)}
</div>
);
};
export default TextEditor;
我在ProjectCreateForm.js中使用上述组件
import React, { Component } from 'react'
import { connect } from 'react-redux';
import { Field ,reduxForm} from 'redux-form';
import {bindActionCreators} from 'redux';
import {createProject} from '../../actions/projectActions'
import TextField from '../ui/TextField';
import TextEditor from '../ui/TextEditor';
import Switch from '../ui/Switch';
import MyDatePicker from '../ui/MyDatePicker'
import {Editor, EditorState,} from 'draft-js';
class ProjectCreateForm extends Component {
constructor() {
super();
this.state={
description:''
}
}
submitCreateProjectForm=(values)=>{
values.description=this.state.description;
return this.props.createProject(values,this.props.history);
}
update=(editorValue)=> {
this.setState({
description:editorValue
});
}
render() {
const {handleSubmit,submitting}= this.props;
return(
<div className="row">
<div className="col-lg-10" style={{marginTop:'78px'}}>
<div className="container">
<h3 className="mb-3">Create New Project</h3>
<div className="card bg-light">
<div className="card-body">
<form method="POST" noValidate className="needs-validation" onSubmit={handleSubmit(this.submitCreateProjectForm)}>
<Field name="name" id="name" placeholder="Project Name" component={TextField} required/>
<Field name="identifier" id="identifier" placeholder="Project Identifier (Example : MYPROJECT)" component={TextField} required/>
<Field name="description" id="description" placeholder="Project Description" component={TextEditor} update={ this.update }/>
<Field name="is_public" id="is_public" label="Project Access" label_off="Private" label_on="Public" component={Switch}/>
<Field name="start_date" id="start_Date" component={MyDatePicker} />
<button type="submit" className="btn btn-primary" disabled={submitting}>CREATE PROJECT</button>
</form>
</div>
</div>
</div>
</div>
</div>
)
}
}
const mapStateToProps = (state, ownProps) => ({
project:state.project
})
const mapDispatchToProps =dispatch=>{
return bindActionCreators({
createProject
},dispatch)
}
ProjectCreateForm=connect(mapStateToProps,mapDispatchToProps)(ProjectCreateForm);
ProjectCreateForm=reduxForm({form:'createProjectForm'})(ProjectCreateForm);
export default ProjectCreateForm;
以下JSON.stringify
个内容已保存在数据库中。
现在我有 ProjectSummary 组件,在这里我想将保存的描述显示为HTML。
import React, { Component } from 'react'
import {connect} from 'react-redux'
import {bindActionCreators} from 'redux'
import {getProjectByID} from '../../actions/projectActions'
import Loader from '../ui/Loader'
import {Editor, ConvertFromRaw} from 'draft-js'
import TextEditor from '../ui/TextEditor'
import {stateToHTML} from 'draft-js-export-html';
class ProjectSummary extends Component {
componentDidMount() {
let id = this.props.match.params.projectid;
this.props.getProjectByID(id,this.props.history)
}
render() {
const {current_project,loading} = this.props.projects
let content;
if(current_project==null)
{
content=<Loader style={{marginTop:'120px'}}/>
}else{
//const storedState = ConvertFromRaw(JSON.parse(current_project.description));
console.log(stateToHTML(current_project.description));
content=(
<div>
<div className="row">
<div className="col-lg-10" style={{marginTop:'78px'}}>
<div className="container">
<h3 className="mb-3 pb-2 border-bottom">{current_project.name}</h3>
</div>
</div>
</div>
</div>
)
}
return content;
}
}
const mapStateToProps = (state, ownProps) => ({
projects:state.projects
})
const mapDispatchToProps =dispatch=> {
return bindActionCreators({
getProjectByID
},dispatch)
}
export default connect(mapStateToProps,mapDispatchToProps)(ProjectSummary);
我搜索了许多类似的问题:
React & Draft.js - convertFromRaw not working
Render html saved from draft-js
我已经尝试过了,但是没有为我工作。出现以下错误: