作者热门文章
- html - 出于某种原因,IE8 对我的 Sass 文件中继承的 html5 CSS 不友好?
- JMeter 在响应断言中使用 span 标签的问题
- html - 在 :hover and :active? 上具有不同效果的 CSS 动画
- html - 相对于居中的 html 内容固定的 CSS 重复背景?
我正在处理这个 react 表排序,当用户点击表头时它需要对表进行排序,排序工作正常但问题是我每秒通过 SignalR hub 接收新数据并设置状态 udata
到新数据。当用户单击表格标题时,它会对表格进行排序,但会再次返回到由新数据更改的新状态。并将已排序的表取消为未排序。
有什么方法可以保持排序状态并仍然接收数据?
我是新手,不胜感激
constructor() {
super()
this.state = {
udata: [],
sort: {
column: null,
direction: 'desc',
},
}
}
componentDidMount() {
let connection = new signalR.HubConnectionBuilder()
.withUrl('/signalserver')
.build()
connection
.start()
.then(function() {})
.catch(function(err) {
return console.error(err.toString())
})
connection.on(
'APIChannel',
function(data) {
this.setState({udata: data})
}.bind(this),
)
async function start() {
try {
await connection.start()
console.log('connected')
} catch (err) {
console.log(err)
setTimeout(() => start(), 5000)
}
}
connection.onclose(async () => {
await start()
})
}
onSort(column) {
return function(e) {
let direction = this.state.sort.direction
if (this.state.sort.column === column) {
// Change the sort direction if the same column is sorted.
direction = this.state.sort.direction === 'asc' ? 'desc' : 'asc'
}
// Sort ascending.
const sortedData = this.state.udata.sort((a, b) => {
if (column === 'appName') {
// This sorts strings taking into consideration numbers in strings.
// e.g., Account 1, Account 2, Account 10. Normal sorting would sort it Account 1, Account 10, Account 2.
const collator = new Intl.Collator(undefined, {
numeric: true,
sensitivity: 'base',
})
return collator.compare(a.appName, b.appName)
} else {
return a.contractValue - b.contractValue
}
})
// Reverse the order if direction is descending.
if (direction === 'desc') {
sortedData.reverse()
}
// Set the new state.
this.setState({
udata: sortedData,
sort: {
column,
direction,
},
})
}.bind(this) // Bind "this" again because the onSort function is returning another function.
}
renderItem(item, key) {
const itemRows = [
<tr onClick={clickCallback} key={'row-data-' + key}>
<td>{item.appName}</td>
<td>
<h6 className="text-muted">
<i
className={
'fa fa-circle text-c-' +
(item.appState === 'STARTED' ? 'green' : 'red') +
' f-10 m-r-15'
}
/>
{item.appState}
</h6>
</td>
<td>{item.spaceName}</td>
<td>
<h6 className="text-muted">{item.orgName}</h6>
</td>
<td>
<h6 className="text-muted">
{new Date(item.appUpdatedAt).toLocaleString()}
</h6>
</td>
</tr>,
]
return itemRows
}
render() {
let allItemRows = []
this.state.udata.forEach((item, key) => {
const perItemRows = this.renderItem(item, key)
allItemRows = allItemRows.concat(perItemRows)
})
return (
<Aux>
<Row>
<Table hover responsive>
<thead>
<tr>
<th className="sortable" onClick={this.onSort('appName')}>
{' '}
Account Name
</th>
<th> State</th>
<th> Space</th>
<th> Organization</th>
<th className="sortable" onClick={this.onSort('appUpdatedAt')}>
{' '}
Updated At
</th>
</tr>
</thead>
<tbody> {allItemRows}</tbody>
</Table>
</Row>
</Aux>
)
}
最佳答案
将函数的排序部分移到新的函数中:
const sortData = (data, column, direction) => {
// Sort ascending.
const sortedData = data.sort((a, b) => {
if (column === 'appName') {
const collator = new Intl.Collator(undefined, {
numeric: true,
sensitivity: 'base',
})
return collator.compare(a.appName, b.appName)
} else {
return a.contractValue - b.contractValue
}
})
// Reverse the order if direction is descending.
if (direction === 'desc') {
return sortedData.reverse()
}
return sortedData
}
您可以在使用 newData 设置状态之前在 componentDidMount
中使用此函数,也可以在 onSort
函数中使用此函数。
onSort(column) {
return function(e) {
let direction = this.state.sort.direction
if (this.state.sort.column === column) {
// Change the sort direction if the same column is sorted.
direction = this.state.sort.direction === 'asc' ? 'desc' : 'asc'
}
// Sort ascending.
const sortedData = this.sortData(this.state.udata, column, direction)
// Set the new state.
this.setState({
udata: sortedData,
sort: {
column,
direction,
},
})
}.bind(this) // Bind "this" again because the onSort function is returning another function.
}
componentDidMount:
componentDidMount() {
// Code
connection.on(
'APIChannel',
function(data) {
let sortedData = []
if (this.state.sort.column) {
sortedData = this.sortData(data, this.state.sort.column,
this.state.sort.direction)
} else {
sortedData = data
}
this.setState({udata: sortedData})
}.bind(this),
)
// Rest of the code
}
编辑:
import React, { Component } from "react";
import { Row, Col, Form, Card, Table, Tab, Nav } from "react-bootstrap";
import Aux from "../../hoc/_Aux";
import * as signalR from "@aspnet/signalr";
class Dashboard extends Component {
constructor() {
super();
this.state = {
udata: [],
sysdata: [],
expandedRows: [],
user: "active",
system: "",
data: [],
UserFilters: {
appState: [],
orgName: [],
spaceName: []
},
SysFilters: {
appState: []
},
intervalId: 0, //Scroll on top feature
sort: {
column: null,
direction: "desc"
}
};
}
sortData = (data, column, direction) => {
// Sort ascending.
const sortedData = data.sort((a, b) => {
if (column === 'appName') {
const collator = new Intl.Collator(undefined, {
numeric: true,
sensitivity: 'base',
})
return collator.compare(a.appName, b.appName)
} else {
return a.contractValue - b.contractValue
}
})
// Reverse the order if direction is descending.
if (direction === 'desc') {
return sortedData.reverse()
}
return sortedData
};
componentDidMount() {
let connection = new signalR.HubConnectionBuilder()
.withUrl("/signalserver")
.build();
connection
.start()
.then(function () { })
.catch(function (err) {
return console.error(err.toString());
});
connection.on(
"SBUserBrodcasting",
function (data) {
let sortedData = [];
if (this.state.sort.column) {
sortedData = this.sortData(
data,
this.state.sort.column,
this.state.sort.direction
);
} else {
sortedData = data;
}
this.setState({ udata: sortedData });
}.bind(this)
);
connection.on(
"SBSystemBrodcasting",
function (data) {
this.setState({ sysdata: data });
}.bind(this)
);
async function start() {
try {
await connection.start();
console.log("connected");
} catch (err) {
console.log(err);
setTimeout(() => start(), 5000);
}
}
connection.onclose(async () => {
await start();
});
}
onSort(column) {
return function (e) {
let direction = this.state.sort.direction;
if (this.state.sort.column === column) {
// Change the sort direction if the same column is sorted.
direction = this.state.sort.direction === "asc" ? "desc" : "asc";
}
// Sort ascending.
const sortedData = this.sortData(this.state.udata, column, direction);
// Set the new state.
this.setState({
udata: sortedData,
sort: {
column,
direction
}
});
}.bind(this); // Bind "this" again because the onSort function is returning another function.
}
scrollStep() {
if (window.pageYOffset === 0) {
clearInterval(this.state.intervalId);
}
window.scroll(0, window.pageYOffset - this.props.scrollStepInPx);
}
scrollToTop() {
let intervalId = setInterval(
this.scrollStep.bind(this),
this.props.delayInMs
);
this.setState({ intervalId: intervalId });
}
FilterUserArray = (array, UserFilters) => {
let getValue = value =>
typeof value === "string" ? value.toUpperCase() : value;
const filterKeys = Object.keys(UserFilters);
return array.filter(item => {
// validates all filter criteria
return filterKeys.every(key => {
// ignores an empty filter
if (!UserFilters[key].length) return true;
return UserFilters[key].find(
filter => getValue(filter) === getValue(item[key])
);
});
});
};
FilterSysArray = (array, SysFilters) => {
let getValue = value =>
typeof value === "string" ? value.toUpperCase() : value;
const filterKeys = Object.keys(SysFilters);
return array.filter(item => {
// validates all filter criteria
return filterKeys.every(key => {
// ignores an empty filter
if (!SysFilters[key].length) return true;
return SysFilters[key].find(
filter => getValue(filter) === getValue(item[key])
);
});
});
};
HandleRowClick(rowId) {
const currentExpandedRows = this.state.expandedRows;
const isRowCurrentlyExpanded = currentExpandedRows.includes(rowId);
const newExpandedRows = isRowCurrentlyExpanded
? currentExpandedRows.filter(id => id !== rowId)
: currentExpandedRows.concat(rowId);
this.setState({ expandedRows: newExpandedRows });
}
SpaceRenderFilterList(item, key) {
const itemRows = [
<li key={"li-data-" + key}>
<Form.Check
custom
type="checkbox"
value={item}
id={"SBSpace-" + item}
label={item}
onChange={this.UserAppSpaceFilter.bind(this)}
/>
</li>
];
return itemRows;
}
OrgRenderFilterList(item, key) {
const itemRows = [
<li key={"li-data-" + key}>
<Form.Check
custom
type="checkbox"
value={item}
id={"SBOrg-" + item}
label={item}
onChange={this.UserAppOrgFilter.bind(this)}
/>
</li>
];
return itemRows;
}
RenderItem(item, key) {
const clickCallback = () => this.HandleRowClick(key);
const itemRows = [
<tr onClick={clickCallback} key={"row-data-" + key}>
<td>{item.appName}</td>
<td>
<h6 className="text-muted">
<i
className={
"fa fa-circle text-c-" +
(item.appState === "STARTED" ? "green" : "red") +
" f-10 m-r-15"
}
/>
{item.appState}
</h6>
</td>
<td>{item.spaceName}</td>
<td>
<h6 className="text-muted">{item.orgName}</h6>
</td>
<td>
<h6 className="text-muted">
{new Date(item.appUpdatedAt).toLocaleString()}
</h6>
</td>
</tr>
];
if (this.state.expandedRows.includes(key)) {
itemRows.push(
<tr key={"row-expanded-" + key}>
<td colSpan="6">
<Card className="card-event">
<Card.Body>
<div className="row align-items-center justify-content-center">
<div className="col">
<h5 className="m-0">Upcoming Event</h5>
</div>
<div className="col-auto">
<label className="label theme-bg2 text-white f-14 f-w-400 float-right">
34%
</label>
</div>
</div>
<h2 className="mt-2 f-w-300">
45<sub className="text-muted f-14">Competitors</sub>
</h2>
<h6 className="text-muted mt-3 mb-0">
You can participate in event{" "}
</h6>
<i className="fa fa-angellist text-c-purple f-50" />
</Card.Body>
</Card>
</td>
</tr>
);
}
return itemRows;
}
onClickfn = () => {
this.setState({ user: "active", system: "inactive" });
};
onClickfnsys = () => {
this.setState({ user: "inactive", system: "active" });
};
UserAppStateFilter(e) {
let index;
// current array of options
const options = this.state.UserFilters.appState;
// check if the check box is checked or unchecked
if (e.target.checked) {
// add the numerical value of the checkbox to options array
options.push(e.target.value);
} else {
// or remove the value from the unchecked checkbox from the array
index = options.indexOf(e.target.value);
options.splice(index, 1);
}
// update the state with the new array of options
this.setState({
UserFilters: { ...this.state.UserFilters, appState: options }
});
}
UserAppSpaceFilter(e) {
let index;
// current array of options
const options = this.state.UserFilters.spaceName;
// check if the check box is checked or unchecked
if (e.target.checked) {
// add the numerical value of the checkbox to options array
options.push(e.target.value);
} else {
// or remove the value from the unchecked checkbox from the array
index = options.indexOf(e.target.value);
options.splice(index, 1);
}
// update the state with the new array of options
this.setState({
UserFilters: { ...this.state.UserFilters, spaceName: options }
});
}
UserAppOrgFilter(e) {
let index;
// current array of options
const options = this.state.UserFilters.orgName;
// check if the check box is checked or unchecked
if (e.target.checked) {
// add the numerical value of the checkbox to options array
options.push(e.target.value);
} else {
// or remove the value from the unchecked checkbox from the array
index = options.indexOf(e.target.value);
options.splice(index, 1);
}
// update the state with the new array of options
this.setState({
UserFilters: { ...this.state.UserFilters, orgName: options }
});
}
SysAppStateFilter(e) {
let index;
// current array of options
const options = this.state.SysFilters.appState;
// check if the check box is checked or unchecked
if (e.target.checked) {
// add the numerical value of the checkbox to options array
options.push(e.target.value);
} else {
// or remove the value from the unchecked checkbox from the array
index = options.indexOf(e.target.value);
options.splice(index, 1);
}
// update the state with the new array of options
this.setState({
SysFilters: { ...this.state.SysFilters, appState: options }
});
}
render() {
let Spacefilterlist = [];
Array.from(new Set(this.state.udata.map(item => item.spaceName))).forEach(
(item, key) => {
const perItemRows = this.SpaceRenderFilterList(item, key);
Spacefilterlist = Spacefilterlist.concat(perItemRows);
}
);
let Orgfilterlist = [];
Array.from(new Set(this.state.udata.map(item => item.orgName))).forEach(
(item, key) => {
const perItemRows = this.OrgRenderFilterList(item, key);
Orgfilterlist = Orgfilterlist.concat(perItemRows);
}
);
let allItemRows = [];
this.FilterUserArray(this.state.udata, this.state.UserFilters).forEach(
(item, key) => {
const perItemRows = this.RenderItem(item, key);
allItemRows = allItemRows.concat(perItemRows);
}
);
let sysallItemRows = [];
this.FilterSysArray(this.state.sysdata, this.state.SysFilters).forEach(
(item, key) => {
const perItemRows = this.RenderItem(item, key);
sysallItemRows = sysallItemRows.concat(perItemRows);
}
);
return (
<Aux>
<Row>
<Col sm={12}>
<Tab.Container defaultActiveKey="user">
<Row>
<Col sm={2}>
<Nav variant="pills" className="flex-column">
<Nav.Item>
<Nav.Link eventKey="user" onClick={this.onClickfn}>
User
</Nav.Link>
</Nav.Item>
<Nav.Item>
<Nav.Link eventKey="system" onClick={this.onClickfnsys}>
System
</Nav.Link>
</Nav.Item>
</Nav>
<br />
<Card
style={{
display: this.state.user === "active" ? "" : "none"
}}
>
<Tab.Pane eventKey="user">
<Card.Header>
<Card.Title as="h5">Filters</Card.Title>
</Card.Header>
<Card.Body>
<h6>By State</h6>
<hr />
<ul className="list-inline m-b-0">
<Form.Group onReset={this.handleFormReset}>
<li>
<Form.Check
custom
type="checkbox"
id="checkbox1"
value="STARTED"
label="STARTED"
onChange={this.UserAppStateFilter.bind(this)}
/>
</li>
<li>
<Form.Check
custom
type="checkbox"
id="checkbox2"
value="STOPPED"
label="STOPPED"
onChange={this.UserAppStateFilter.bind(this)}
/>
</li>
</Form.Group>
</ul>
<h6>By Space</h6>
<hr />
<ul className="list-inline m-b-0">
<Form.Group>{Spacefilterlist}</Form.Group>
</ul>
<h6>By Organization</h6>
<hr />
<ul className="list-inline m-b-0">
<Form.Group>{Orgfilterlist}</Form.Group>
</ul>
</Card.Body>
</Tab.Pane>
</Card>
<Card>
<Tab.Pane
eventKey="system"
style={{
display: this.state.system === "active" ? "" : "none"
}}
>
<Card.Header>
<Card.Title as="h5">Filters</Card.Title>
</Card.Header>
<Card.Body>
<h6>By State</h6>
<hr />
<ul className="list-inline m-b-0">
<Form.Group>
<li>
<Form.Check
custom
type="checkbox"
id="chec1"
value="STARTED"
label="STARTED"
onChange={this.SysAppStateFilter.bind(this)}
/>
</li>
<li>
<Form.Check
custom
type="checkbox"
id="chec2"
value="STOPPED"
label="STOPPED"
onChange={this.SysAppStateFilter.bind(this)}
/>
</li>
</Form.Group>
</ul>
</Card.Body>
</Tab.Pane>
</Card>
</Col>
<Col sm={10}>
<Tab.Content>
<Tab.Pane eventKey="user">
<Table hover responsive>
<thead>
<tr>
<th
className="sortable"
onClick={this.onSort("appName")}
>
Account Name
</th>
<th>State</th>
<th>Space</th>
<th>Organization</th>
<th
className="sortable"
onClick={this.onSort("appUpdatedAt")}
>
Updated At
</th>
</tr>
</thead>
<tbody>{allItemRows}</tbody>
</Table>
</Tab.Pane>
<Tab.Pane eventKey="system">
<Table hover responsive>
<thead>
<tr>
<th>App Name</th>
<th>State</th>
<th>Space</th>
<th>Organization</th>
<th>Updated At</th>
</tr>
</thead>
<tbody>{sysallItemRows}</tbody>
</Table>
</Tab.Pane>
</Tab.Content>
</Col>
</Row>
</Tab.Container>
</Col>
<button
id="myBtn"
title="Back to top"
className="scroll"
onClick={() => {
this.scrollToTop();
}}
>
<span className="feather icon-chevron-up" />
</button>
</Row>
</Aux>
);
}
}
export default Dashboard;
关于javascript - 如何在保持数据状态有序的同时保持接收数据?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/57871695/
我是一名优秀的程序员,十分优秀!