我正在尝试在React中创建一个表组件,其中列和行都是动态的并随时间而变化。但是,当数据发生更改时,React会抛出意外的DOM突变不变违规。
这是展示问题http://jsfiddle.net/69z2wepo/1797/的小提琴。如您所见,初始渲染后数据发生了变化,React无法再跟踪DOM的状态。
代码在这里:
var Table = React.createClass({
getDefaultProps: function() {
return {
data: [
{
colA: { value: 'foo' },
},
],
};
},
render: function() {
return (
<table>
<thead>
<tr> {
Object.keys(this.props.data[0]).map(function(key) {
return (<th>{ key }</th>);
}.bind(this))
} </tr>
</thead>
<tbody> {
this.props.data.map(function(item) {
return (<tr> { Object.keys(item).map(function(key) {
return (
<td>{ item[key] }</td>
);
}.bind(this)) } </tr>);
}.bind(this))
} </tbody>
</table>
);
}
});
var Main = React.createClass({
componentWillMount: function() {
setTimeout(function() {
var data = [
{
colA: {
value: 'foobar',
},
},
];
this.setState({
data: data,
});
}.bind(this), 3000);
},
getInitialState: function() {
var data = [
{
colA: {
value: 'foo',
},
colB: {
value: 'bar',
}
},
{
colA: {
value: 'foo',
},
colB: {
value: 'bar',
}
}
];
return {
data: data,
};
},
render: function() {
return (<Table data={ this.state.data } />);
},
});
React.render(<Main />, document.body);
console.log(React.renderToString(<Table/>));
我尝试过各种方式添加关键属性来跟踪各种元素,似乎没有什么可以解决这个问题。
使用renderToString渲染表组件显示React正在表的各个级别插入一堆元素。这是可能的原因吗?请参阅此处呈现的DOM:
<table data-reactid=".1" data-react-checksum="1098817301">
<thead data-reactid=".1.0">
<tr data-reactid=".1.0.0">
<span data-reactid=".1.0.0.0"> </span>
<th data-reactid=".1.0.0.1:0">colA</th>
<span data-reactid=".1.0.0.2"> </span>
</tr>
</thead>
<tbody data-reactid=".1.1">
<span data-reactid=".1.1.0"> </span>
<tr data-reactid=".1.1.1:0">
<span data-reactid=".1.1.1:0.0"> </span>
<td data-reactid=".1.1.1:0.1:0"><span data-reactid=".1.1.1:0.1:0.$value:0">foo</span></td>
<span data-reactid=".1.1.1:0.2"> </span>
</tr>
<span data-reactid=".1.1.2"> </span>
</tbody>
</table>
答案 0 :(得分:5)
原来问题在于你的缩进。如果你在一个新行开始花括号{}
(在JSX中编写javascript括号),你的代码就可以了。不知道为什么会发生这种情况。
jsfiddle:http://jsfiddle.net/cwn2nebs/
var Table = React.createClass({
getDefaultProps: function() {
return {
data: [
{
colA: { value: 'foo' },
},
],
};
},
render: function() {
return (
<table>
<thead>
<tr>
{
Object.keys(this.props.data[0]).map(function(key, idx) {
return (<th key={ idx } >{ key }</th>);
}.bind(this))
}
</tr>
</thead>
<tbody>
{
this.props.data.map(function(item, idx) {
return (
<tr key={ idx }>
{
Object.keys(item).map(function(key, i) {
return (
<td key={ i }>{ item[key] }</td>
);
}.bind(this)) }
</tr>);
}.bind(this))
}
</tbody>
</table>
);
}
});
var Main = React.createClass({
componentWillMount: function() {
setTimeout(function() {
var data = [
{
colA: {
value: 'foobar',
}
},
];
this.setState({
data: data,
});
}.bind(this), 3000);
},
getInitialState: function() {
var data = [
{
colA: {
value: 'foo',
},
colB: {
value: 'bar',
}
},
{
colA: {
value: 'foo',
},
colB: {
value: 'bar',
}
}
];
return {
data: data,
};
},
render: function() {
return (<Table data={ this.state.data } />);
},
});
React.render(<Main />, document.body);
<强>更新强>
使用JSX compiler我试图将部分代码转换为普通JS:
render: function() {
return (
React.createElement("table", null,
React.createElement("thead", null,
React.createElement("tr", null, " ",
Object.keys(this.props.data[0]).map(function(key, idx) {
return (React.createElement("th", {key: idx }, key ));
}.bind(this)),
" ")
),
React.createElement("tbody", null, " ",
this.props.data.map(function(item, idx) {
return (React.createElement("tr", {key: idx }, " ", Object.keys(item).map(function(key, i) {
return (
React.createElement("td", {key: i }, item[key] )
);
}.bind(this)), " "));
}.bind(this)),
" ")
)
);
}
这就是React.createElement
的工作原理:
React.createElement(type, props, children);
请注意tr
元素的空子项:
React.createElement("tr", null, " ",
Object.keys(this.props.data[0]).map(function(key, idx) {
return (React.createElement("th", {key: idx }, key ));
}.bind(this)),
" ")
但是在新行上使用花括号,编译后的代码看起来像这样(没有空(&#34;&#34;)子代):
React.createElement("tr", null,
Object.keys(this.props.data[0]).map(function(key, idx) {
return (React.createElement("th", {key: idx }, key ));
}.bind(this))
)
我相信当您发现时,React会将" "
个孩子转换为导致此问题的span
个元素。