2015-10-05 158 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