如何在保持数据状态有序的同时保持接收数据?
How to keep receiving data while keeping data in state in order?
我正在研究这个反应 table 当用户点击 table header 它需要对 table 进行排序时排序,排序工作正常但问题是我每秒通过 SignalR 集线器接收新数据并将状态 udata
设置为新数据。当用户单击 table header 时,它会对 table 进行排序,但会再次返回到由新数据更改的新状态。并取消排序 table 回到未排序。
有什么方法可以保持排序状态并仍然接收数据?
我是新手,不胜感激
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>
)
}
使用父组件执行请求并将未排序的值和排序顺序值传递给子组件。
子组件(table 组件最有可能)将根据排序顺序值显示数据。
目前每次更改状态值时都会安装您的组件
添加一个名为 componentWillReceiveProps(nextProps)
的生命周期方法,或者您也可以使用 static getDerivedStateFromProps(props, state)
在此方法内部执行排序,这样当有新数据可用时,它会自动与那里原来的那个。因此,您的所有其他代码都保持不变,新数据只是在排序中占据了应有的位置。
将函数的排序部分移至新函数:
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;
我正在研究这个反应 table 当用户点击 table header 它需要对 table 进行排序时排序,排序工作正常但问题是我每秒通过 SignalR 集线器接收新数据并将状态 udata
设置为新数据。当用户单击 table header 时,它会对 table 进行排序,但会再次返回到由新数据更改的新状态。并取消排序 table 回到未排序。
有什么方法可以保持排序状态并仍然接收数据?
我是新手,不胜感激
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>
)
}
使用父组件执行请求并将未排序的值和排序顺序值传递给子组件。 子组件(table 组件最有可能)将根据排序顺序值显示数据。
目前每次更改状态值时都会安装您的组件
添加一个名为 componentWillReceiveProps(nextProps)
的生命周期方法,或者您也可以使用 static getDerivedStateFromProps(props, state)
在此方法内部执行排序,这样当有新数据可用时,它会自动与那里原来的那个。因此,您的所有其他代码都保持不变,新数据只是在排序中占据了应有的位置。
将函数的排序部分移至新函数:
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;