【问题标题】:Changing table structure causes React to lose track of the DOM更改表结构会导致 React 失去对 DOM 的跟踪
【发布时间】:2015-04-03 03:09:32
【问题描述】:

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

【问题讨论】:

  • 我没有在那个小提琴中看到错误?
  • 有一个 setTimeout 调用正在运行。您可能需要稍等片刻。
  • ...不,没有? jsfiddle.net/reactjs/69z2wepo我认为你的链接有误。
  • 你是对的。我很抱歉。正确的链接是jsfiddle.net/69z2wepo/1797
  • 您也可以删除&gt;{ 之间的空格。所以&lt;tr&gt;{ 而不是&lt;tr&gt; { ,与结束&gt;} 相同。其原因主要是因为foo &lt;b&gt;bar&lt;/b&gt; 不应该呈现为 foobar

标签: javascript reactjs html-table


【解决方案1】:

原来问题出在你的缩进上。如果您在新行上开始大括号{}(在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)), 
 " ")

但是用大括号换行,编译后的代码看起来像这样(没有空的 (" ") 子级):

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 元素,这是导致问题的原因,正如您所发现的那样。

【讨论】:

  • 好收获。是什么促使您尝试新的产品线?
  • 是的,非常好。我最不想尝试的事情。
  • 为了记录,我括号的放置导致 React(或 JSX?)插入跨度,Chrome 将有助于删除。更改放置不再在 renderToString 上生成跨度。至少我认为是对的。
猜你喜欢
  • 1970-01-01
  • 2021-09-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2010-09-13
  • 1970-01-01
相关资源
最近更新 更多