ui: Fix useEffect dependency warnings.

This commit is contained in:
Orne Brocaar 2024-04-01 12:13:41 +01:00
parent 984a86cc7a
commit 9453ab7e00
10 changed files with 248 additions and 236 deletions

View File

@ -12,29 +12,29 @@ interface IProps {
function Admin(props: PropsWithChildren<IProps>) { function Admin(props: PropsWithChildren<IProps>) {
const [admin, setAdmin] = useState<boolean>(false); const [admin, setAdmin] = useState<boolean>(false);
const setIsAdmin = () => {
if (!props.isDeviceAdmin && !props.isGatewayAdmin && !props.isTenantAdmin) {
setAdmin(SessionStore.isAdmin());
} else {
if (props.tenantId === undefined) {
throw new Error("No tenantId is given");
}
if (props.isTenantAdmin) {
setAdmin(SessionStore.isAdmin() || SessionStore.isTenantAdmin(props.tenantId));
}
if (props.isDeviceAdmin) {
setAdmin(SessionStore.isAdmin() || SessionStore.isTenantDeviceAdmin(props.tenantId));
}
if (props.isGatewayAdmin) {
setAdmin(SessionStore.isAdmin() || SessionStore.isTenantGatewayAdmin(props.tenantId));
}
}
};
useEffect(() => { useEffect(() => {
const setIsAdmin = () => {
if (!props.isDeviceAdmin && !props.isGatewayAdmin && !props.isTenantAdmin) {
setAdmin(SessionStore.isAdmin());
} else {
if (props.tenantId === undefined) {
throw new Error("No tenantId is given");
}
if (props.isTenantAdmin) {
setAdmin(SessionStore.isAdmin() || SessionStore.isTenantAdmin(props.tenantId));
}
if (props.isDeviceAdmin) {
setAdmin(SessionStore.isAdmin() || SessionStore.isTenantDeviceAdmin(props.tenantId));
}
if (props.isGatewayAdmin) {
setAdmin(SessionStore.isAdmin() || SessionStore.isTenantGatewayAdmin(props.tenantId));
}
}
};
SessionStore.on("change", setIsAdmin); SessionStore.on("change", setIsAdmin);
setIsAdmin(); setIsAdmin();

View File

@ -1,4 +1,4 @@
import React, { useState, useEffect } from "react"; import React, { useState, useEffect, useCallback } from "react";
import { Table } from "antd"; import { Table } from "antd";
import { ColumnsType } from "antd/es/table"; import { ColumnsType } from "antd/es/table";
@ -23,21 +23,24 @@ function DataTable(props: IProps) {
const [rows, setRows] = useState<object[]>([]); const [rows, setRows] = useState<object[]>([]);
const [loading, setLoading] = useState<boolean>(true); const [loading, setLoading] = useState<boolean>(true);
const onChangePage = (page: number, pz?: number | void) => { const onChangePage = useCallback(
setLoading(true); (page: number, pz?: number | void) => {
setLoading(true);
if (!pz) { if (!pz) {
pz = pageSize; pz = pageSize;
} }
props.getPage(pz, (page - 1) * pz, (totalCount: number, rows: object[]) => { props.getPage(pz, (page - 1) * pz, (totalCount: number, rows: object[]) => {
setCurrentPage(page); setCurrentPage(page);
setTotalCount(totalCount); setTotalCount(totalCount);
setRows(rows); setRows(rows);
setPageSize(pz || 0); setPageSize(pz || 0);
setLoading(false); setLoading(false);
}); });
}; },
[props, pageSize],
);
const onShowSizeChange = (page: number, pageSize: number) => { const onShowSizeChange = (page: number, pageSize: number) => {
onChangePage(page, pageSize); onChangePage(page, pageSize);
@ -53,7 +56,7 @@ function DataTable(props: IProps) {
useEffect(() => { useEffect(() => {
onChangePage(currentPage, pageSize); onChangePage(currentPage, pageSize);
}, [props, currentPage, pageSize]); }, [props, currentPage, pageSize, onChangePage]);
const { getPage, refreshKey, ...otherProps } = props; const { getPage, refreshKey, ...otherProps } = props;
let loadingProps = undefined; let loadingProps = undefined;

View File

@ -1,4 +1,4 @@
import React, { useState, useEffect } from "react"; import React, { useState, useEffect, useCallback } from "react";
import { Link, useLocation, useNavigate } from "react-router-dom"; import { Link, useLocation, useNavigate } from "react-router-dom";
import { Menu, MenuProps, Typography } from "antd"; import { Menu, MenuProps, Typography } from "antd";
@ -67,7 +67,7 @@ function SideMenu() {
navigate(`/tenants/${value}`); navigate(`/tenants/${value}`);
}; };
const parseLocation = () => { const parseLocation = useCallback(() => {
const path = location.pathname; const path = location.pathname;
const tenantRe = /\/tenants\/([\w-]{36})/g; const tenantRe = /\/tenants\/([\w-]{36})/g;
const match = tenantRe.exec(path); const match = tenantRe.exec(path);
@ -134,7 +134,7 @@ function SideMenu() {
if (/\/tenants\/[\w-]{36}\/applications.*/g.exec(path)) { if (/\/tenants\/[\w-]{36}\/applications.*/g.exec(path)) {
setSelectedKey("tenant-applications"); setSelectedKey("tenant-applications");
} }
}; }, [location.pathname, tenantId]);
useEffect(() => { useEffect(() => {
SessionStore.on("tenant.change", setTenant); SessionStore.on("tenant.change", setTenant);
@ -150,11 +150,11 @@ function SideMenu() {
return () => { return () => {
SessionStore.removeListener("tenant.change", setTenant); SessionStore.removeListener("tenant.change", setTenant);
}; };
}, []); }, [parseLocation]);
useEffect(() => { useEffect(() => {
parseLocation(); parseLocation();
}, [location]); }, [location, parseLocation]);
let items: MenuProps["items"] = []; let items: MenuProps["items"] = [];

View File

@ -26,6 +26,16 @@ function ApplicationLoader(props: IProps) {
const [measurementKeys, setMeasurementKeys] = useState<string[]>([]); const [measurementKeys, setMeasurementKeys] = useState<string[]>([]);
useEffect(() => { useEffect(() => {
const loadApplication = () => {
let req = new GetApplicationRequest();
req.setId(applicationId!);
ApplicationStore.get(req, (resp: GetApplicationResponse) => {
setApplication(resp.getApplication());
setMeasurementKeys(resp.getMeasurementKeysList());
});
};
ApplicationStore.on("change", loadApplication); ApplicationStore.on("change", loadApplication);
loadApplication(); loadApplication();
@ -34,16 +44,6 @@ function ApplicationLoader(props: IProps) {
}; };
}, [applicationId]); }, [applicationId]);
const loadApplication = () => {
let req = new GetApplicationRequest();
req.setId(applicationId!);
ApplicationStore.get(req, (resp: GetApplicationResponse) => {
setApplication(resp.getApplication());
setMeasurementKeys(resp.getMeasurementKeysList());
});
};
const app = application; const app = application;
if (!app) { if (!app) {
return null; return null;

View File

@ -32,111 +32,111 @@ function ListIntegrations(props: IProps) {
const [available, setAvailable] = useState<any[]>([]); const [available, setAvailable] = useState<any[]>([]);
useEffect(() => { useEffect(() => {
const loadIntegrations = () => {
let req = new ListIntegrationsRequest();
req.setApplicationId(props.application.getId());
ApplicationStore.listIntegrations(req, (resp: ListIntegrationsResponse) => {
let configured: any[] = [];
let available: any[] = [];
const includes = (integrations: IntegrationListItem[], kind: IntegrationKind) => {
for (let x of integrations) {
if (x.getKind() === kind) {
return true;
}
}
return false;
};
// AWS SNS
if (includes(resp.getResultList(), IntegrationKind.AWS_SNS)) {
configured.push(<AwsSnsCard application={props.application} />);
} else {
available.push(<AwsSnsCard application={props.application} add />);
}
// Azure Service-Bus
if (includes(resp.getResultList(), IntegrationKind.AZURE_SERVICE_BUS)) {
configured.push(<AzureServiceBusCard application={props.application} />);
} else {
available.push(<AzureServiceBusCard application={props.application} add />);
}
// GCP Pub/Sub
if (includes(resp.getResultList(), IntegrationKind.GCP_PUB_SUB)) {
configured.push(<GcpPubSubCard application={props.application} />);
} else {
available.push(<GcpPubSubCard application={props.application} add />);
}
// HTTP
if (includes(resp.getResultList(), IntegrationKind.HTTP)) {
configured.push(<HttpCard application={props.application} />);
} else {
available.push(<HttpCard application={props.application} add />);
}
// IFTTT
if (includes(resp.getResultList(), IntegrationKind.IFTTT)) {
configured.push(<IftttCard application={props.application} />);
} else {
available.push(<IftttCard application={props.application} add />);
}
// InfluxDB
if (includes(resp.getResultList(), IntegrationKind.INFLUX_DB)) {
configured.push(<InfluxdbCard application={props.application} />);
} else {
available.push(<InfluxdbCard application={props.application} add />);
}
// MQTT
if (includes(resp.getResultList(), IntegrationKind.MQTT_GLOBAL)) {
configured.push(<MqttCard application={props.application} />);
}
// myDevices
if (includes(resp.getResultList(), IntegrationKind.MY_DEVICES)) {
configured.push(<MyDevicesCard application={props.application} />);
} else {
available.push(<MyDevicesCard application={props.application} add />);
}
// Pilot Things
if (includes(resp.getResultList(), IntegrationKind.PILOT_THINGS)) {
configured.push(<PilotThingsCard application={props.application} />);
} else {
available.push(<PilotThingsCard application={props.application} add />);
}
// Semtech LoRa Cloud
if (includes(resp.getResultList(), IntegrationKind.LORA_CLOUD)) {
configured.push(<LoRaCloudCard application={props.application} />);
} else {
available.push(<LoRaCloudCard application={props.application} add />);
}
// ThingsBoard
if (includes(resp.getResultList(), IntegrationKind.THINGS_BOARD)) {
configured.push(<ThingsBoardCard application={props.application} />);
} else {
available.push(<ThingsBoardCard application={props.application} add />);
}
setConfigured(configured);
setAvailable(available);
});
};
ApplicationStore.on("integration.delete", loadIntegrations); ApplicationStore.on("integration.delete", loadIntegrations);
loadIntegrations(); loadIntegrations();
return () => { return () => {
ApplicationStore.removeAllListeners("integration.delete"); ApplicationStore.removeAllListeners("integration.delete");
}; };
}, []); }, [props.application]);
const loadIntegrations = () => {
let req = new ListIntegrationsRequest();
req.setApplicationId(props.application.getId());
ApplicationStore.listIntegrations(req, (resp: ListIntegrationsResponse) => {
let configured: any[] = [];
let available: any[] = [];
const includes = (integrations: IntegrationListItem[], kind: IntegrationKind) => {
for (let x of integrations) {
if (x.getKind() === kind) {
return true;
}
}
return false;
};
// AWS SNS
if (includes(resp.getResultList(), IntegrationKind.AWS_SNS)) {
configured.push(<AwsSnsCard application={props.application} />);
} else {
available.push(<AwsSnsCard application={props.application} add />);
}
// Azure Service-Bus
if (includes(resp.getResultList(), IntegrationKind.AZURE_SERVICE_BUS)) {
configured.push(<AzureServiceBusCard application={props.application} />);
} else {
available.push(<AzureServiceBusCard application={props.application} add />);
}
// GCP Pub/Sub
if (includes(resp.getResultList(), IntegrationKind.GCP_PUB_SUB)) {
configured.push(<GcpPubSubCard application={props.application} />);
} else {
available.push(<GcpPubSubCard application={props.application} add />);
}
// HTTP
if (includes(resp.getResultList(), IntegrationKind.HTTP)) {
configured.push(<HttpCard application={props.application} />);
} else {
available.push(<HttpCard application={props.application} add />);
}
// IFTTT
if (includes(resp.getResultList(), IntegrationKind.IFTTT)) {
configured.push(<IftttCard application={props.application} />);
} else {
available.push(<IftttCard application={props.application} add />);
}
// InfluxDB
if (includes(resp.getResultList(), IntegrationKind.INFLUX_DB)) {
configured.push(<InfluxdbCard application={props.application} />);
} else {
available.push(<InfluxdbCard application={props.application} add />);
}
// MQTT
if (includes(resp.getResultList(), IntegrationKind.MQTT_GLOBAL)) {
configured.push(<MqttCard application={props.application} />);
}
// myDevices
if (includes(resp.getResultList(), IntegrationKind.MY_DEVICES)) {
configured.push(<MyDevicesCard application={props.application} />);
} else {
available.push(<MyDevicesCard application={props.application} add />);
}
// Pilot Things
if (includes(resp.getResultList(), IntegrationKind.PILOT_THINGS)) {
configured.push(<PilotThingsCard application={props.application} />);
} else {
available.push(<PilotThingsCard application={props.application} add />);
}
// Semtech LoRa Cloud
if (includes(resp.getResultList(), IntegrationKind.LORA_CLOUD)) {
configured.push(<LoRaCloudCard application={props.application} />);
} else {
available.push(<LoRaCloudCard application={props.application} add />);
}
// ThingsBoard
if (includes(resp.getResultList(), IntegrationKind.THINGS_BOARD)) {
configured.push(<ThingsBoardCard application={props.application} />);
} else {
available.push(<ThingsBoardCard application={props.application} add />);
}
setConfigured(configured);
setAvailable(available);
});
};
return ( return (
<Row gutter={24}> <Row gutter={24}>

View File

@ -1,4 +1,4 @@
import React, { useState, useEffect } from "react"; import React, { useState, useEffect, useCallback } from "react";
import { Link } from "react-router-dom"; import { Link } from "react-router-dom";
import moment from "moment"; import moment from "moment";
@ -33,11 +33,50 @@ function DeviceDashboard(props: IProps) {
const [deviceLinkMetrics, setDeviceLinkMetrics] = useState<GetDeviceLinkMetricsResponse | undefined>(undefined); const [deviceLinkMetrics, setDeviceLinkMetrics] = useState<GetDeviceLinkMetricsResponse | undefined>(undefined);
const [deviceLinkMetricsLoaded, setDeviceLinkMetricsLoaded] = useState<boolean>(false); const [deviceLinkMetricsLoaded, setDeviceLinkMetricsLoaded] = useState<boolean>(false);
useEffect(() => { const loadDeviceMetrics = useCallback(
loadMetrics(); (start: Date, end: Date, agg: Aggregation) => {
}, [props, metricsAggregation]); let startPb = new Timestamp();
let endPb = new Timestamp();
const loadMetrics = () => { startPb.fromDate(start);
endPb.fromDate(end);
let req = new GetDeviceMetricsRequest();
req.setDevEui(props.device.getDevEui());
req.setStart(startPb);
req.setEnd(endPb);
req.setAggregation(agg);
DeviceStore.getMetrics(req, (resp: GetDeviceMetricsResponse) => {
setDeviceMetrics(resp);
});
},
[props.device],
);
const loadLinkMetrics = useCallback(
(start: Date, end: Date, agg: Aggregation) => {
let startPb = new Timestamp();
let endPb = new Timestamp();
startPb.fromDate(start);
endPb.fromDate(end);
let req = new GetDeviceLinkMetricsRequest();
req.setDevEui(props.device.getDevEui());
req.setStart(startPb);
req.setEnd(endPb);
req.setAggregation(agg);
DeviceStore.getLinkMetrics(req, (resp: GetDeviceLinkMetricsResponse) => {
setDeviceLinkMetrics(resp);
setDeviceLinkMetricsLoaded(true);
});
},
[props.device],
);
const loadMetrics = useCallback(() => {
const agg = metricsAggregation; const agg = metricsAggregation;
const end = moment(); const end = moment();
let start = moment(); let start = moment();
@ -53,44 +92,11 @@ function DeviceDashboard(props: IProps) {
setDeviceLinkMetricsLoaded(false); setDeviceLinkMetricsLoaded(false);
loadLinkMetrics(start.toDate(), end.toDate(), agg); loadLinkMetrics(start.toDate(), end.toDate(), agg);
loadDeviceMetrics(start.toDate(), end.toDate(), agg); loadDeviceMetrics(start.toDate(), end.toDate(), agg);
}; }, [loadLinkMetrics, loadDeviceMetrics, metricsAggregation]);
const loadDeviceMetrics = (start: Date, end: Date, agg: Aggregation) => { useEffect(() => {
let startPb = new Timestamp(); loadMetrics();
let endPb = new Timestamp(); }, [props, metricsAggregation, loadMetrics]);
startPb.fromDate(start);
endPb.fromDate(end);
let req = new GetDeviceMetricsRequest();
req.setDevEui(props.device.getDevEui());
req.setStart(startPb);
req.setEnd(endPb);
req.setAggregation(agg);
DeviceStore.getMetrics(req, (resp: GetDeviceMetricsResponse) => {
setDeviceMetrics(resp);
});
};
const loadLinkMetrics = (start: Date, end: Date, agg: Aggregation) => {
let startPb = new Timestamp();
let endPb = new Timestamp();
startPb.fromDate(start);
endPb.fromDate(end);
let req = new GetDeviceLinkMetricsRequest();
req.setDevEui(props.device.getDevEui());
req.setStart(startPb);
req.setEnd(endPb);
req.setAggregation(agg);
DeviceStore.getLinkMetrics(req, (resp: GetDeviceLinkMetricsResponse) => {
setDeviceLinkMetrics(resp);
setDeviceLinkMetricsLoaded(true);
});
};
const onMetricsAggregationChange = (e: RadioChangeEvent) => { const onMetricsAggregationChange = (e: RadioChangeEvent) => {
setMetricsAggregation(e.target.value); setMetricsAggregation(e.target.value);

View File

@ -46,6 +46,25 @@ function DeviceLayout(props: IProps) {
const [lastSeenAt, setLastSeenAt] = useState<Date | undefined>(undefined); const [lastSeenAt, setLastSeenAt] = useState<Date | undefined>(undefined);
useEffect(() => { useEffect(() => {
const loadDevice = () => {
let req = new GetDeviceRequest();
req.setDevEui(devEui!);
DeviceStore.get(req, (resp: GetDeviceResponse) => {
setDevice(resp.getDevice());
if (resp.getLastSeenAt() !== undefined) {
setLastSeenAt(resp.getLastSeenAt()!.toDate());
}
let req = new GetDeviceProfileRequest();
req.setId(resp.getDevice()!.getDeviceProfileId());
DeviceProfileStore.get(req, (resp: GetDeviceProfileResponse) => {
setDeviceProfile(resp.getDeviceProfile());
});
});
};
DeviceStore.on("change", loadDevice); DeviceStore.on("change", loadDevice);
loadDevice(); loadDevice();
@ -54,25 +73,6 @@ function DeviceLayout(props: IProps) {
}; };
}, [devEui]); }, [devEui]);
const loadDevice = () => {
let req = new GetDeviceRequest();
req.setDevEui(devEui!);
DeviceStore.get(req, (resp: GetDeviceResponse) => {
setDevice(resp.getDevice());
if (resp.getLastSeenAt() !== undefined) {
setLastSeenAt(resp.getLastSeenAt()!.toDate());
}
let req = new GetDeviceProfileRequest();
req.setId(resp.getDevice()!.getDeviceProfileId());
DeviceProfileStore.get(req, (resp: GetDeviceProfileResponse) => {
setDeviceProfile(resp.getDeviceProfile());
});
});
};
const deleteDevice = () => { const deleteDevice = () => {
let req = new DeleteDeviceRequest(); let req = new DeleteDeviceRequest();
req.setDevEui(devEui!); req.setDevEui(devEui!);

View File

@ -3,7 +3,7 @@ import React, { useState } from "react";
import { Struct } from "google-protobuf/google/protobuf/struct_pb"; import { Struct } from "google-protobuf/google/protobuf/struct_pb";
import { Switch, notification } from "antd"; import { Switch, notification } from "antd";
import { Button, Tabs, Space, Card, Row, Form, Input, InputNumber, Checkbox, Popconfirm } from "antd"; import { Button, Tabs, Space, Card, Row, Form, Input, InputNumber, Popconfirm } from "antd";
import { ColumnsType } from "antd/es/table"; import { ColumnsType } from "antd/es/table";
import { RedoOutlined, DeleteOutlined } from "@ant-design/icons"; import { RedoOutlined, DeleteOutlined } from "@ant-design/icons";
import { Buffer } from "buffer"; import { Buffer } from "buffer";

View File

@ -23,7 +23,7 @@ interface IProps {
} }
function GatewayDashboard(props: IProps) { function GatewayDashboard(props: IProps) {
const [metricsAggregation, setMetricsAggregation] = useState<Aggregation>(Aggregation.DAY); const [metricsAggregation] = useState<Aggregation>(Aggregation.DAY);
const [gatewayMetrics, setGatewayMetrics] = useState<GetGatewayMetricsResponse | undefined>(undefined); const [gatewayMetrics, setGatewayMetrics] = useState<GetGatewayMetricsResponse | undefined>(undefined);
useEffect(() => { useEffect(() => {

View File

@ -1,4 +1,4 @@
import React, { useState, useEffect } from "react"; import React, { useState, useEffect, useCallback } from "react";
import { Form, Input, InputNumber, Row, Col, Button, Tabs, Space, Card } from "antd"; import { Form, Input, InputNumber, Row, Col, Button, Tabs, Space, Card } from "antd";
import { MinusCircleOutlined, PlusOutlined } from "@ant-design/icons"; import { MinusCircleOutlined, PlusOutlined } from "@ant-design/icons";
@ -24,6 +24,29 @@ function GatewayForm(props: IProps) {
const [lonValue, setLonValue] = useState<number>(0); const [lonValue, setLonValue] = useState<number>(0);
const [locationPending, setLocationPending] = useState<boolean>(false); const [locationPending, setLocationPending] = useState<boolean>(false);
const setLocationFields = useCallback(
(lat: number, lon: number) => {
form.setFieldsValue({
location: {
latitude: lat,
longitude: lon,
},
});
},
[form],
);
const getCurrentLocation = useCallback(() => {
setLocationPending(true);
LocationStore.getLocation((loc: [number, number]) => {
setLatValue(loc[0]);
setLonValue(loc[1]);
setLocationPending(false);
setLocationFields(loc[0], loc[1]);
});
}, [setLocationFields]);
useEffect(() => { useEffect(() => {
if (!props.update) { if (!props.update) {
getCurrentLocation(); getCurrentLocation();
@ -34,18 +57,7 @@ function GatewayForm(props: IProps) {
setLonValue(loc.getLongitude()); setLonValue(loc.getLongitude());
} }
} }
}, [props]); }, [props, getCurrentLocation]);
const getCurrentLocation = () => {
setLocationPending(true);
LocationStore.getLocation((loc: [number, number]) => {
setLatValue(loc[0]);
setLonValue(loc[1]);
setLocationPending(false);
setLocationFields(loc[0], loc[1]);
});
};
const onFinish = (values: Gateway.AsObject) => { const onFinish = (values: Gateway.AsObject) => {
const v = Object.assign(props.initialValues.toObject(), values); const v = Object.assign(props.initialValues.toObject(), values);
@ -79,15 +91,6 @@ function GatewayForm(props: IProps) {
setLocationFields(loc.lat, loc.lng); setLocationFields(loc.lat, loc.lng);
}; };
const setLocationFields = (lat: number, lon: number) => {
form.setFieldsValue({
location: {
latitude: lat,
longitude: lon,
},
});
};
const location: [number, number] = [latValue, lonValue]; const location: [number, number] = [latValue, lonValue];
return ( return (