Reputation: 71
I have a page that is a table with several columns. The user has the ability to sort the entries in the table by several different values; when the user clicks on the header, sorting is performed, and a new render of the page is triggered. However, while the sorting is performed correctly, the DOM doesn't render in the right order; some elements in the table switch places, but most stay the same. When inspecting elements using the react plugin for Chrome, React has things in the correct order; it's just that the DOM doesn't actually match what react thinks is going on.
My code is as follows:
var BinData = React.createClass( {
render: function() {
return (
<tr className="databin">
<td className="name">
<span className="name-field">
<span className="databin-name">{this.props.data.Name ? this.props.data.Name: "Databin Name"}</span>
<span className="wdd-icon-edit-name"></span>
</span>
</td>
<td className="id">{this.props.data.ShortID ? this.props.data.ShortID: "None"}</td>
<td className="date">{this.props.data.formatedLatestTimestamp}</td>
<td className="url"><a href={this.props.data.ShortURL}>{this.props.data.ShortURL}</a></td>
<td className="size">{this.props.data.Size}</td>
<td className="owner">{this.props.data.Owner}</td>
<td className="actions"><span className="icon-container"><span className="wdd-icon-wolfram-alpha"><br /></span><span className="text analyze">Analyze in Wolfram|Alpha</span></span><span className="icon-container"><span className="wdd-icon-wolfram-language-small"><br /></span><span className="text explore">Open in Wolfram Language</span></span><span className="icon-container"><span className="wdd-icon-download"><br /></span><span className="text download">Download raw data</span></span><span className="icon-container"><span className="wdd-icon-collaborate"><br /></span><span className="text access">Access settings</span></span><span className="icon-container"><span className="wdd-icon-delete"><br /></span><span className="text delete">Remove from my list</span></span></td>
</tr>
);
}
});
var BinList = React.createClass( {
getInitialState: function() {
return {
sorting: "Date"
};
},
sortByName: function(bins,selected) {
function compare(a,b) {
if (a.Name < b.Name) {
return -1;
}
if (a.Name > b.Name) {
return 1;
}
return 0;
}
if (selected == "My Databins") {
bins.Creator.sort(compare);
}
else if (selected == "Recent Databins") {
bins.Contributor.sort(compare);
}
else {
bins.SharedWithMe.sort(compare);
}
this.setState({sorting: "Name"});
},
sortByDate : function(bins,selected) {
function compare(a,b) {
return b.LatestTimestamp - a.LatestTimestamp;
}
if (selected == "My Databins") {
bins.Creator.sort(compare);
}
else if (selected == "Recent Databins") {
bins.Contributor.sort(compare);
}
else {
bins.SharedWithMe.sort(compare);
}
this.setState({sorting: "Date"});
},
sortByOwner: function(bins,selected) {
function compare(a,b) {
if (a.Owner < b.Owner) {
return -1;
}
if (a.Owner > b.Owner) {
return 1;
}
return 0;
}
if (selected == "My Databins") {
bins.Creator.sort(compare);
}
else if (selected == "Recent Databins") {
bins.Contributor.sort(compare);
}
else {
bins.SharedWithMe.sort(compare);
}
this.setState({sorting: "Owner"});
},
sortBySize: function(bins,selected) {
function compare(a,b) {
return b.Size - a.Size;
}
if (selected == "My Databins") {
bins.Creator.sort(compare);
}
else if (selected == "Recent Databins") {
bins.Contributor.sort(compare);
}
else {
bins.SharedWithMe.sort(compare);
}
this.setState({sorting: "Size"});
},
refresh: function() {
console.log("in refresh");
this.forceUpdate();
},
render: function() {
var rows = [];
if (this.props.selected == "My Databins") {
if (this.props.data.Creator) {
this.props.data.Creator.forEach(function(bin) {
rows.push(<BinData data={bin} key={bin.UUID}/>);
});
};
}
else if (this.props.selected == "Recent Databins") {
if (this.props.data.Contributor) {
this.props.data.Contributor.forEach(function(bin) {
rows.push(<BinData data={bin} key={bin.UUID}/>);
});
};
}
else {
if (this.props.data.SharedWithMe) {
this.props.data.SharedWithMe.forEach(function(bin) {
rows.push(<BinData data={bin} key={bin.UUID}/>);
});
};
}
return (
<section>
{rows.length > 0 ?
<table className="databins-list">
<thead>
<tr className="table-head">
<th className="head-names"><a className="sort descending" href="#" onClick={this.sortByName.bind(this,this.props.data, this.props.selected)}>Databin Name {this.state.sorting == "Name" ? <span className="triangle-down"></span>: ""}</a></th>
<th className="head-ids">Databin ID</th>
<th className="head-dates"><a className="sort" href="#" onClick={this.sortByDate.bind(this,this.props.data,this.props.selected)}>Latest Entry {this.state.sorting == "Date" ? <span className="triangle-down"></span>:""}</a></th>
<th className="head-urls">Databin Url</th>
<th className="head-sizes"><a className="sort" href="#" onClick={this.sortBySize.bind(this,this.props.data,this.props.selected)}>Size {this.state.sorting=="Size" ? <span className="triangle-down"></span>:""}</a></th>
<th className="head-owners"><a className="sort" href="#" onClick={this.sortByOwner.bind(this,this.props.data,this.props.selected)}>Owner {this.state.sorting=="Owner" ? <span className="triangle-down"></span>:""}</a></th>
<th className="head-actions"><a className="refresh" onClick={this.refresh.bind(this)}>Refresh databins <span className="wdd-icon-refresh"></span></a></th>
</tr>
</thead>
<tbody> {rows} </tbody>
</table> :
<AdminZeroState selected={this.props.selected} />
}
</section>
);
}
});
Does anyone have any idea why what is rendered in the DOM doesn't match what react thinks is going on?
Upvotes: 0
Views: 332
Reputation: 3498
I'm not sure exactly why these kinds of problems happen, but I've seen similar redraw errors. I would advise adding a key attribute to the table element. Whenever the key attribute changes, the entire table will re-render. This is useful because I think the problem comes from React and the DOM getting out of sync with things being sorted. If your key value changes whenever things are sorted (including the type of sort in the key name, for example), then I think that will work.
Upvotes: 2