编辑 2015
有人用我的解决方案在 NPM 上做了一个项目:https : //github.com/lovasoa/react-contenteditable
编辑 06/2016:我刚刚遇到了一个新问题,当浏览器尝试“重新格式化”您刚刚提供给他的 html 时会出现这个问题,导致组件总是重新渲染。看
编辑 07/2016:这是我的生产 contentEditable 实现。它有一些react-contenteditable
您可能想要的附加选项,包括:
- 锁定
- 允许嵌入 html 片段的命令式 API
- 重新格式化内容的能力
概括:
FakeRainBrigand 的解决方案在一段时间内对我来说效果很好,直到我遇到新问题。ContentEditables 很痛苦,而且处理 React 并不容易......
这个JSFiddle演示了这个问题。
如您所见,当您键入一些字符并单击 时Clear
,内容并没有被清除。这是因为我们尝试将 contenteditable 重置为最后一个已知的虚拟 dom 值。
所以看起来:
- 您需要
shouldComponentUpdate
防止插入符号位置跳转
- 如果你使用
shouldComponentUpdate
这种方式,你就不能依赖 React 的 VDOM diffing 算法。
所以你需要一个额外的行,这样每当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 仍然过时,它可能不是最有效的代码,但至少它确实有效 :)我的错误已解决
细节:
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。