I would like to sort table rows when clicking on table headers. I have the code working on a regular html/javascript snippet here . However it is not working as a React method. I think the problem is the use of the method .querySelectorAll()
and .parentNode
. I have already replaced the method .getElementById()
with this.refs.rows
by adding a ref to the <tbody>
but I don't think one ref can point to multiple elements (to get all the <td>
elements). I am looking for a similar way to replace both of the methods which don't seem to work. Or is it another problem entirely?
Edit: Added the entire React component as asked in the comments
import React from "react";
import { Row, Col, Table } from "reactstrap";
export class ComparePlayers extends React.Component {
constructor(props) {
super(props);
this.state = {
players: [],
};
}
componentDidMount() {
fetch("/players")
.then((response) => response.json())
.then((state) => {
this.setState({ players: state });
});
}
handleSort(n) {
let rows, switching, i, x, y, shouldSwitch;
switching = true;
while (switching) {
switching = false;
rows = this.refs.rows; /* replaced document.getElementById("myTable").rows; */
for (i = 0; i < rows.length; i++) {
shouldSwitch = false;
x = rows[i].querySelectorAll("td")[n]; // how to replace?
y = rows[i + 1].querySelectorAll("td")[n]; // how to replace?
if (!isNaN(Number(x.innerHTML))) {
if (Number(x.innerHTML) > Number(y.innerHTML)) {
shouldSwitch = true;
break;
}
} else {
if (x.innerHTML.toLowerCase() > y.innerHTML.toLowerCase()) {
shouldSwitch = true;
break;
}
}
}
if (shouldSwitch) {
rows[i].parentNode.insertBefore(rows[i + 1], rows[i]); // how to replace?
switching = true;
}
}
}
render() {
return (
<Row>
<Col>
<Table bordered>
<thead>
<tr>
{this.state.players.length > 0 &&
Object.keys(this.state.players[0]).map((key, id) => (
<th
key={"header_" + key}
onClick={() => this.handleSort(id)}
>
{key.toUpperCase()}
</th>
))}
</tr>
</thead>
<tbody ref="rows">
{this.state.players.length > 0 &&
this.state.players.map((player, id) => {
return (
<tr key={"row" + id}>
{Object.values(player).map((value) => (
<td key={"table_value_" + value}>{value}</td>
))}
</tr>
);
})}
</tbody>
</Table>
</Col>
</Row>
);
}
}
UPDATE AND SOLUTION:
I used @MahdyAslamy 's answer and adapted to my state which is an array of objects. I used this tutorial to sort array of objects according to property values. Here is the final code:
handleSort(el) {
const compareValues = (key) => {
return function innerSort(a, b) {
if (!a.hasOwnProperty(key) || !b.hasOwnProperty(key)) {
// property doesn't exist on either object
return 0;
}
const varA = typeof a[key] === "string" ? a[key].toUpperCase() : a[key];
const varB = typeof b[key] === "string" ? b[key].toUpperCase() : b[key];
let comparison = 0;
if (varA > varB) {
comparison = 1;
} else if (varA < varB) {
comparison = -1;
}
return comparison;
};
};
const sorted = this.state.players.sort(compareValues(el));
this.setState({
players: sorted
});
}
It's not good approach to sort table by changing tags position on dom. react suggest to use states and usual component life cycle for changing appearance.
for example:
class Game extends React.Component { constructor(props) { super(props); this.state = { list: [5,6,8,9,6,5,4,22,4] } } render() { return ( <div className="game"> <button onClick={() => this.setState({ list: this.state.list.sort() })}>sort</button> <ul> {this.state.list.map(el => <li>{el}</li>)} </ul> </div> ); } } // ======================================== ReactDOM.render( <Game />, document.getElementById('root') );
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script> <div id="root"></div>
Only sort your list and then will show sorted list on render.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.