2015 年编辑
有人用我的解决方案在 NPM 上做了一个项目:https://github.com/lovasoa/react-contenteditable
编辑 06/2016: 我刚刚遇到了一个新问题,当浏览器尝试“重新格式化”您刚刚提供给他的 html 时会出现该问题,从而导致组件总是重新呈现。 See
Edit 07/2016:这是我的生产 contentEditable 实现。除了react-contenteditable,它还有一些您可能想要的附加选项,包括:
- 锁定
- 命令式 API 允许嵌入 html 片段
- 能够重新格式化内容
总结:
FakeRainBrigand 的解决方案在我遇到新问题之前一直很好用。 ContentEditables 很痛苦,而且用 React 处理起来并不容易......
这个JSFiddle 说明了问题。
如你所见,当你输入一些字符并点击Clear时,内容并没有被清除。这是因为我们尝试将 contenteditable 重置为最后一个已知的虚拟 dom 值。
看来:
- 您需要
shouldComponentUpdate 来防止插入符号位置跳转
- 如果你以这种方式使用
shouldComponentUpdate,你就不能依赖 React 的 VDOM 差异算法。
所以你需要一个额外的行,这样每当shouldComponentUpdate 返回yes 时,你就可以确定DOM 内容实际上已经更新了。
所以这里的版本增加了componentDidUpdate,变成:
var ContentEditable = React.createClass({
render: function(){
return <div id="contenteditable"
onInput={this.emitChange}
onBlur={this.emitChange}
contentEditable
dangerouslySetInnerHTML={{__html: this.props.html}}></div>;
},
shouldComponentUpdate: function(nextProps){
return nextProps.html !== this.getDOMNode().innerHTML;
},
componentDidUpdate: function() {
if ( this.props.html !== this.getDOMNode().innerHTML ) {
this.getDOMNode().innerHTML = this.props.html;
}
},
emitChange: function(){
var html = this.getDOMNode().innerHTML;
if (this.props.onChange && html !== this.lastHtml) {
this.props.onChange({
target: {
value: html
}
});
}
this.lastHtml = html;
}
});
Virtual dom 已经过时了,它可能不是最有效的代码,但至少它确实有效 :) My bug is resolved
详情:
1) 如果你设置 shouldComponentUpdate 以避免插入符号跳转,那么 contenteditable 永远不会重新渲染(至少在击键时)
2) 如果组件在击键时从不重新渲染,那么 React 会为这个 contenteditable 保留一个过时的虚拟 dom。
3) 如果 React 在其虚拟 dom 树中保留了过期版本的 contenteditable,那么如果你尝试将 contenteditable 重置为虚拟 dom 中过期的值,那么在虚拟 dom diff 期间,React 将计算出有没有要应用于 DOM 的更改!
这主要发生在:
- 您最初有一个空的 contenteditable(shouldComponentUpdate=true,prop="",previous vdom=N/A),
- 用户键入了一些文本,而您阻止了呈现 (shouldComponentUpdate=false,prop=text,previous vdom="")
- 在用户单击验证按钮后,您希望清空该字段(shouldComponentUpdate=false,prop="",previous vdom="")
- 由于新生成的和旧的 vdom 都是 "",因此 React 不会触及 dom。