如何在React.js中使用CKEditor,使React能够识别它?

16

我试过使用componentWillMount和componentDidMount在React上下文中初始化CKEditor,但无论尝试哪种组合都似乎不起作用。除了切换编辑器之外,是否有人找到了解决方法?


谢谢,jibees。我自己也发现了这个选项,看起来它是我的最后选择。 - Slbox
我使用它,非常满意。你应该试一试! - jibees
2
我最终选择了DraftJS作为我的解决方案,因为它是由Facebook支持的,而且很不可能被抛弃。感谢你的帮助,jibees! - Slbox
1
请查看此视频:https://www.youtube.com/watch?v=feUYwoLhE_4 确实非常酷 ;) - jibees
Draft 显示出了很大的潜力,但仍然显得非常前沿。文档很少,尝试使用第三方插件包甚至更糟糕。我花了很多时间在 Draft 上尝试让事情正常工作,这可能会让人抓狂。一些文档甚至提到了 npm 模块,但这些模块在 npm 中甚至不存在。奇怪。简而言之,除非 Facebook 给予更多支持,否则我不确定它是否已经准备好在 Facebook 以外的主流市场上使用。 - Dave Munger
显示剩余2条评论
5个回答

28

我在Npm上发布了一个包,用于在React中使用CKEditor。只需一行代码即可将其集成到您的项目中。

Github链接- https://github.com/codeslayer1/react-ckeditor.

如何使用?

  • 使用npm install react-ckeditor-component --save命令安装该包。
  • 然后在您的React应用程序中包含该组件,并传递内容和任何其他需要的props(所有props都在Github页面上列出):

<CKEditor activeClass="editor" content={this.state.content} onChange={this.updateContent} />

该包使用CKEditor的默认构建版本,但您也可以使用自定义构建版本以及您喜欢的任何插件。它还包括一个示例应用程序。希望您会发现它有用。


1
这真的很酷。尽管我个人不再需要这个,但由于其简单性和MIT许可证,我将授予您答案。 - Slbox
2
谢谢。我在尝试将CKEditor集成到我的项目中时也遇到了这个问题。意识到没有处理这个问题的开源软件包,所以我创建了一个。 - codeslayer1
2
这对我来说解决了问题。我也能够进行文件浏览和上传。这个软件包是一个真正的救星。 - Gurnzbot
1
@ZeGregg 没有,目前还没有。一旦CKEditor5发布稳定版本,我会尝试添加支持。它目前处于alpha测试阶段。 - codeslayer1

16

Sage在他的答案中描述了一个非常棒的解决方案。这对我来说救命了,因为我刚刚开始使用React,并且我需要它来实现我的需求。不过,我还是改变了实现方式,同时采纳了Jared的建议(使用componentDidMount)。此外,我的需求是要有一个改变回调函数,像这样:

组件的使用:

<CKEditor value={this.props.value} onChange={this.onChange}/>

将此内容添加至index.html

<script src="//cdn.ckeditor.com/4.6.1/basic/ckeditor.js"></script>

使用以下组件代码:

import React, {Component} from "react";

export default class CKEditor extends Component {
  constructor(props) {
    super(props);
    this.componentDidMount = this.componentDidMount.bind(this);
  }

  render() {
    return (
      <textarea name="editor" cols="100" rows="6" defaultValue={this.props.value}></textarea>
    )
  }

  componentDidMount() {
    let configuration = {
      toolbar: "Basic"
    };
    CKEDITOR.replace("editor", configuration);
    CKEDITOR.instances.editor.on('change', function () {
      let data = CKEDITOR.instances.editor.getData();
      this.props.onChange(data);
    }.bind(this));
  }
}

再次感谢 Sage!


以下是基本版本的改进版,支持在同一页面上使用多个 CKEditor 实例:

import React, {Component} from "react";

export default class CKEditor extends Component {
  constructor(props) {
    super(props);
    this.elementName = "editor_" + this.props.id;
    this.componentDidMount = this.componentDidMount.bind(this);
  }

  render() {
    return (
      <textarea name={this.elementName} defaultValue={this.props.value}></textarea>
    )
  }

  componentDidMount() {
    let configuration = {
      toolbar: "Basic"
    };
    CKEDITOR.replace(this.elementName, configuration);
    CKEDITOR.instances[this.elementName].on("change", function () {
      let data = CKEDITOR.instances[this.elementName].getData();
      this.props.onChange(data);
    }.bind(this));
  }
}
请注意,这还需要传递一些唯一的ID:
<CKEditor id={...} value={this.props.value} onChange={this.onChange}/>

1
太棒了!感谢更新。对于未来阅读此内容的任何其他人,我最终使用了React-RTE,但是转而使用SlateJS,它不仅仅是即插即用,而且非常灵活。 - Slbox
完美。没有更多的话了。谢谢! - modernator
回到这个问题,将 "blur" 更改为 "change",因为 "blur" 没有考虑在 CKEditor 中输入内容,然后立即提交表单(例如按钮点击)的情况。 - Sander Verhagen
@SanderVerhagen 当触发React更新的"change"事件导致CKEditor变得缓慢时,我正在寻找一种像"change"一样快速的解决方案。 - Reed G. Law
@ReedG.Law,我无法确认速度慢的问题,但显然里程可能会有所不同,改进总是受欢迎的! - Sander Verhagen
显示剩余3条评论

7

这是一个用于显示P段落文本的React组件。如果用户想要编辑段落中的文本,他们可以单击它,这将会附加一个CKEditor实例。当用户完成在编辑器实例中更改文本后,“失焦”事件将触发,将CKEditor数据传输到状态属性并销毁CKEditor实例。

import React, {PropTypes, Component} from 'react';

export default class ConditionalWYSIWYG extends Component {
    constructor(props) {
        super(props);
        this.state = {
            field_name:this.props.field_name,
            field_value:this.props.field_value,
            showWYSIWYG:false
        };
        this.beginEdit = this.beginEdit.bind(this);
        this.initEditor = this.initEditor.bind(this);
    }
    render() {
        if ( this.state.showWYSIWYG  ) {
            var field = this.state.field_name;
            this.initEditor(field);
            return (
                <textarea name='editor' cols="100" rows="6" defaultValue={unescape(this.state.field_value)}></textarea>
            )
        } else {
            return (
                <p className='description_field' onClick={this.beginEdit}>{unescape(this.state.field_value)}</p>
            )
        }
    }
    beginEdit() {
        this.setState({showWYSIWYG:true})
    }
    initEditor(field) {
        var self = this;

        function toggle() {
            CKEDITOR.replace("editor", { toolbar: "Basic", width: 870, height: 150 });
            CKEDITOR.instances.editor.on('blur', function() {

                let data = CKEDITOR.instances.editor.getData();
                self.setState({
                    field_value:escape(data),
                    showWYSIWYG:false
                });
                self.value = data;
                CKEDITOR.instances.editor.destroy();
            });
        }
        window.setTimeout(toggle, 100);
    }
}

self.value = data 允许我通过简单的 ref 从父组件中检索文本。

window.setTimeout(); 给 React 时间去执行它该做的事情。如果没有这个延迟,控制台会报出 Cannot read property 'getEditor' of undefined 的错误。

希望这能帮到您。


3
render 应该是一个纯函数,因此调用 initEditor 应该移出 render 方法,而是在 componentDidMount 和 componentDidUpdate 中调用,因为组件在那时已经渲染到 DOM 中了。这也应该消除包装 CKEditor 初始化的 setTimeout 调用的需要。 - Jared Siirila

2

只需在index.html中引用ckeditor.js,并使用window.CKEDITOR来使用它。不要像React组件文档中那样直接使用CKEDITOR

只需阅读ckeditor.js的第一行,您就会发现关于CKEDITOR定义的内容。


0
感谢Sage、Sander和其他人。我只是想为CKEditor的“inline”模式贡献一个版本。
首先,使用以下方法禁用CKEditor的“自动内联”行为...
CKEDITOR.disableAutoInline = true

然后,对于实际组件...

import React, {Component} from 'react';

export default class CKEditor extends Component {
    constructor(props) {
        super(props);
        this.elementName = "editor_" + this.props.id;
        this.componentDidMount = this.componentDidMount.bind(this);
        this.onInput = this.onInput.bind(this);
    }

    onInput(data) {
        console.log('onInput: ' + data);
    }

    render() {
        return (
            <div 
                contentEditable={true} 
                suppressContentEditableWarning
                className="rte"
                id={this.elementName}> 
                {this.props.value}</div>
        )
    }

    componentDidMount() {
        let configuration = {
            toolbar: "Basic"
        };
        CKEDITOR.inline(this.elementName, configuration);
        CKEDITOR.instances[this.elementName].on("change", function() {
            let data = CKEDITOR.instances[this.elementName].getData();
            this.onInput(data);
        }.bind(this));
    }
}

使用方法大致如下:

<CKEditor id="102" value="something" onInput={this.onInput} />

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接