2015-10-05 124 views
0

我有一个非常大的动态窗体,它是基于通过AJAX收集的数据呈现的。React js重新渲染大量组件

有一个包含表格的表单。该表包含一个标题,然后包含一系列行(由状态数据确定)。每行然后有一系列单元(也由状态数据确定)。每个单元都包含一个复选框。

我的问题是关于在哪里维护大量复选框的状态。最初,我的方法是使复选框控制组件,每个单元维护其复选框的状态。

我也尝试在父窗体组件本身上实现选择框的状态作为状态。

前一种方法的问题是,当我提交表单时,我没有表单级别的状态,因为它埋在了单元格后代中。我能以某种方式得到这个状态吗? findDomNode似乎只是当前组件,而不是级联。此外,标题单元格具有复选框,只要检查了列中的所有行,就会检查该复选框,但此解决方案无法让我具有更新该信息的信息。

后一种方法的问题是,当我对一个复选框进行更改时,状态更改导致表单组件被重新呈现,这又导致整个表被重新呈现(或至少让它的渲染方法被调用)。这是非常耗时的,对于一张大桌子(通常可能是100x50),需要非常不友好的时间来放弃。

在一天结束时,检查复选框的影响应该非常小。复选框本身应该更新,如果列的完全选定状态已更改,则可能需要更新列标题才能选中/取消选中。而已。

我已经剥去了很多代码,但这基本上是我正在使用的。了解表格中的单元格复选框正在跟踪应将哪些标签应用于图像可能会有帮助。表格标题单元跟踪哪些标记(从图像名称提取的文本)被映射到哪些标记。当这种情况发生变化时,这一栏就不得不重新提出(另一个我还没有解决的问题,如果没有完全重新渲染一切,我怀疑它会再次变得困难)。

var AutoTagForm = React.createClass({ 
    getInitialState: function() { 
    return {data: { 
     tags: [], 
     images: {}, 
     tokens: [], 
     tokenTagMap: {} 
     } 
    }; 
    }, 
    componentDidMount: function() { 
    $.ajax({ 
     url: this.props.url, 
     type: "POST", 
     data: { imageIds: this.props.images }, 
     dataType: 'json', 
     cache: false, 
     success: function(data) { 
     this.setState({data: data}); 
     }.bind(this), 
     error: function(xhr, status, err) { 
     console.error(this.props.url, status, err.toString()); 
     }.bind(this) 
    }); 
    }, 

    onSubmit: function(e) { 
    e.preventDefault() 

    // Submit the updates to the server 
    }, 

    cellCheckedChange: function(image, tag) { 
    // If the image has the tag, add it. Otherwise remove it 

    var tagIndex = image.checkedTags.indexOf(tag) 

    // TODO Perhaps it is safer to find the image object in this.state.data? 
    if (tagIndex === -1) { 
     image.checkedTags.push(tag); 
    } else { 
     image.checkedTags.splice(tagIndex, 1); 
    } 

    this.setState({ 
     data: this.state.data 
    }) 

    }, 

    render: function() { 
    var images = this.state.data.images; 
    var tokenTagMap = this.state.data.tokenTagMap; 
    var cellCheckedChange = this.cellCheckedChange; 

    var rowNodes = Object.keys(images).map(function(key){ 
     if (images.hasOwnProperty(key)) { 
     var image = images[key]; 

     return (
      <AutoTagImageRow key={image.id} 
          image={image} 
          tokenTagMap={tokenTagMap} 
          cellCheckedChange={cellCheckedChange} /> 
     ); 
     } 
    }); 

    return (
     <form ref="form" onSubmit={this.onSubmit} id="updateAllForm" className={'autotagForm'}> 
     <div> 
      <input type="submit" 
       value="Apply" /> 
     </div> 

     <div> 
      <table> 
      <thead> 
       <AutoTagHeaderRow tags={this.state.data.tags} 
           tokens={this.state.data.tokens} 
           tokenTagMap={this.state.data.tokenTagMap} /> 
      </thead> 

      <tbody> 
       {rowNodes} 
      </tbody> 

      </table> 
      </div> 
     </form> 
    ); 
    } 
}); 


var AutoTagImageRow = React.createClass({ 
    render: function() { 
    var image = this.props.image; 
    var tokenTagMap = this.props.tokenTagMap; 
    var cellCheckedChange = this.props.cellCheckedChange; 

    var cellNodes = Object.keys(tokenTagMap).map(function(token){ 
     if (tokenTagMap.hasOwnProperty(token)) { 
     return (
      <AutoTagImageRowCell image={image} 
           token={token} 
           tokenTagMap={tokenTagMap} 
           cellCheckedChange={cellCheckedChange} /> 
     ); 
     } 
    }); 

    return (
     <tr> 
     {cellNodes} 
     <td>{image.clientPath}</td> 
     </tr> 
    ); 
    } 
}); 


var AutoTagImageRowCell = React.createClass({ 

    isTagged: function() { 
    var image = this.props.image; 
    var token = this.props.token; 
    var tokenTagMap = this.props.tokenTagMap; 
    var tag = tokenTagMap[token]; 

    return (image.tags.indexOf(tag) !== -1); 
    }, 

    isChecked: function() { 
    var image = this.props.image; 
    var token = this.props.token; 
    var tokenTagMap = this.props.tokenTagMap; 
    var tag = tokenTagMap[token]; 

    return (image.checkedTags.indexOf(tag) !== -1); 
    }, 

    handleCheckedChange: function() { 
    var image = this.props.image; 
    var token = this.props.token; 
    var tokenTagMap = this.props.tokenTagMap; 
    var tag = tokenTagMap[token]; 

    this.props.cellCheckedChange(image, tag); 
    }, 

    render: function() { 

    var className = ''; 
    if (this.isTagged()) { 
     className = 'success'; 
    } 

    return (
     <td className={className}> 
     <input type="checkbox" 
       checked={this.isChecked()} 
       onChange={this.handleCheckedChange} /> 
     </td> 
    ); 
    } 
}); 

我是React的新手,可能会做出一些糟糕的设计决定!这与我以前通过使用简单的JavaScript处理事情的方式截然不同,而且我很想遵循React哲学,但是我觉得它几乎立即出错了,因为这是我尝试做的第一件事通过教程。

回答

1

好的。经过一些额外的研究,我发现这个问题很好的解决方案。

关键是要能够确定哪些组件需要重新渲染。这是我以前考虑过的事情,但是我国政府为确定图像行是否需要重新提交而进行的比较几乎与重新渲染本身一样密集。

输入。我把我的JavaScript对象变成了不可变的映射,并将我的数组变成了不可变的集合。就像这样:

for (var key in data.images) { 
     // data.images[key] = Immutable.fromJS(data.images[key]); 

     data.images[key] = Immutable.fromJS(
     data.images[key], function (key, value) { 
      var isIndexed = Immutable.Iterable.isIndexed(value); 
      return isIndexed ? value.toSet() : value.toMap(); 
     } 
    ); 
    } 

我并没有改变整体的data只是还没有,因为是尚未更新,使用不可变的有数据的其他位,但是这将是可能的。

后来,当我从我的复选框中的一个处理一个onchange我在形式层面实现它,像这样:

cellCheckedChange: function(imageId, tagId) { 
    var images = this.state.data.images; 
    var image = images[imageId]; 

    // If the image has the tag, add it. Otherwise remove it 
    var checked = image.hasIn(['checkedTags', tagId]); 

    images[imageId] = image.update('checkedTags', function(value) { 
     return checked ? value.delete(tagId): value.add(tagId); 
    }); 

    this.setState({ 
     data: this.state.data 
    }); 

    }, 

所以更新到不可变对象会导致创建一个新的对象。这将对不可变状态结构产生向上的级联效应,因为任何对孩子的更改都需要新的父级。任何没有直接或通过向上级联更新的状态仍然是相同的对象,这很重要,因为您可以独立地推断这些状态。

最后,我在我的图像行组件更新,只有更新如果图像是一个新的对象(这将是如果图像或任何后代已更新):

var AutoTagImageRow = React.createClass({ 
    render: function() { 
    ... 
    }, 
    // Only update an image row that has had a modified state 
    shouldComponentUpdate: function(nextProps, nextState) { 
    return nextProps.image !== this.props.image; 
    } 
    ... 
}); 

所以当发生更新,在操作javascript状态时会发生更多的工作,但是作为回报,我可以推断出什么已经非常轻松地改变了!

正如我上面提到的,不可替换的不可变对象的嵌套层次结构的任何部分都是相同的对象,因此可以将这些对象与旧结构的子对象进行比较,以查看它们是否已用结果他们没有。这是一个独立的例子。

var Immutable = require('immutable'); 

var input = { 
    '1': 'aaa', 
    '2': [1,2,3,4] 
}; 

var x = Immutable.fromJS(
    input, function (key, value) { 
    var isIndexed = Immutable.Iterable.isIndexed(value); 
    return isIndexed ? value.toSet() : value.toMap(); 
    } 
); 

// Get another reference to x 
var y = x; 

// Update the array stored in '2' 
x = x.update('2', function(v) { 
    return v.add(7); 
}); 

// object '1' remains the same JS object 
console.log(x.get('1') === y.get('1')); //True 

// object '2' is a new JS object 
console.log(x.get('2') === y.get('2')); //False 

// Overall 'x' is a new JS object 
console.log(x === y); //False