更改表结构会导致React失去对DOM的跟踪

时间:2015-02-03 19:24:50

标签: javascript reactjs html-table

我正在尝试在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>

1 个答案:

答案 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个元素。

相关问题