Extracting HOC tracked component class base for re-use (#1829)

* Extracting HOC tracked component class  for re-use

* addressing review comments

Co-authored-by: Ladislav Bitto <labitto@microsoft.com>
Co-authored-by: Nev <54870357+MSNev@users.noreply.github.com>
This commit is contained in:
Ladislav Bitto 2022-05-04 17:28:00 -07:00 коммит произвёл GitHub
Родитель 9d4e266421
Коммит 4391b212f8
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
2 изменённых файлов: 93 добавлений и 72 удалений

Просмотреть файл

@ -3,7 +3,7 @@
import { IReactExtensionConfig } from "./Interfaces/IReactExtensionConfig";
import ReactPlugin from "./ReactPlugin";
import withAITracking from "./withAITracking";
import withAITracking, { AITrackedComponentBase } from "./withAITracking";
import AppInsightsErrorBoundary from "./AppInsightsErrorBoundary"
import {
AppInsightsContext,
@ -20,5 +20,6 @@ export {
AppInsightsContext,
useAppInsightsContext,
useTrackEvent,
useTrackMetric
useTrackMetric,
AITrackedComponentBase
};

Просмотреть файл

@ -2,10 +2,92 @@
// Licensed under the MIT License.
import { IMetricTelemetry } from '@microsoft/applicationinsights-common';
import { CoreUtils } from '@microsoft/applicationinsights-core-js';
import { dateNow } from '@microsoft/applicationinsights-core-js';
import * as React from 'react';
import ReactPlugin from './ReactPlugin';
/**
* Higher-order component base class to hook Application Insights tracking
* in a React component's lifecycle.
*/
export abstract class AITrackedComponentBase<P> extends React.Component<P> {
protected _mountTimestamp: number = 0;
protected _firstActiveTimestamp: number = 0;
protected _idleStartTimestamp: number = 0;
protected _lastActiveTimestamp: number = 0;
protected _totalIdleTime: number = 0;
protected _idleCount: number = 0;
protected _idleTimeout: number = 5000;
protected _intervalId?: any;
protected _componentName: string;
protected _reactPlugin: ReactPlugin;
public constructor(props: P, reactPlugin: ReactPlugin, componentName: string) {
super(props);
this._reactPlugin = reactPlugin;
this._componentName = componentName;
}
public componentDidMount() {
this._mountTimestamp = dateNow();
this._firstActiveTimestamp = 0;
this._totalIdleTime = 0;
this._lastActiveTimestamp = 0;
this._idleStartTimestamp = 0;
this._idleCount = 0;
this._intervalId = setInterval(() => {
if (this._lastActiveTimestamp > 0 && this._idleStartTimestamp === 0 && dateNow() - this._lastActiveTimestamp >= this._idleTimeout) {
this._idleStartTimestamp = dateNow();
this._idleCount++;
}
}, 100);
}
public componentWillUnmount() {
if (this._mountTimestamp === 0) {
throw new Error('withAITracking:componentWillUnmount: mountTimestamp is not initialized.');
}
if (this._intervalId) {
clearInterval(this._intervalId);
}
if (this._firstActiveTimestamp === 0) {
return;
}
const engagementTime = this.getEngagementTimeSeconds();
const metricData: IMetricTelemetry = {
average: engagementTime,
name: 'React Component Engaged Time (seconds)',
sampleCount: 1
};
const additionalProperties: { [key: string]: any } = { 'Component Name': this._componentName };
this._reactPlugin.trackMetric(metricData, additionalProperties);
}
protected trackActivity = (e: React.SyntheticEvent<any>): void => {
if (this._firstActiveTimestamp === 0) {
this._firstActiveTimestamp = dateNow();
this._lastActiveTimestamp = this._firstActiveTimestamp;
} else {
this._lastActiveTimestamp = dateNow();
}
if (this._idleStartTimestamp > 0) {
const lastIdleTime = this._lastActiveTimestamp - this._idleStartTimestamp;
this._totalIdleTime += lastIdleTime;
this._idleStartTimestamp = 0;
}
}
private getEngagementTimeSeconds(): number {
return (dateNow() - this._firstActiveTimestamp - this._totalIdleTime - this._idleCount * this._idleTimeout) / 1000;
}
}
/**
* Higher-order component function to hook Application Insights tracking
* in a React component's lifecycle.
@ -18,63 +100,20 @@ import ReactPlugin from './ReactPlugin';
export default function withAITracking<P>(reactPlugin: ReactPlugin, Component: React.ComponentType<P>, componentName?: string, className?: string): React.ComponentClass<P> {
if (componentName === undefined || componentName === null || typeof componentName !== 'string') {
componentName = Component.prototype &&
Component.prototype.constructor &&
Component.prototype.constructor.name ||
'Unknown';
componentName = Component.prototype &&
Component.prototype.constructor &&
Component.prototype.constructor.name ||
'Unknown';
}
if (className === undefined || className === null || typeof className !== 'string') {
className = '';
}
return class extends React.Component<P> {
private _mountTimestamp: number = 0;
private _firstActiveTimestamp: number = 0;
private _idleStartTimestamp: number = 0;
private _lastActiveTimestamp: number = 0;
private _totalIdleTime: number = 0;
private _idleCount: number = 0;
private _idleTimeout: number = 5000;
private _intervalId?: any;
return class extends AITrackedComponentBase<P> {
public componentDidMount() {
this._mountTimestamp = CoreUtils.dateNow();
this._firstActiveTimestamp = 0;
this._totalIdleTime = 0;
this._lastActiveTimestamp = 0;
this._idleStartTimestamp = 0;
this._idleCount = 0;
this._intervalId = setInterval(() => {
if (this._lastActiveTimestamp > 0 && this._idleStartTimestamp === 0 && CoreUtils.dateNow() - this._lastActiveTimestamp >= this._idleTimeout) {
this._idleStartTimestamp = CoreUtils.dateNow();
this._idleCount++;
}
}, 100);
}
public componentWillUnmount() {
if (this._mountTimestamp === 0) {
throw new Error('withAITracking:componentWillUnmount: mountTimestamp is not initialized.');
}
if (this._intervalId) {
clearInterval(this._intervalId);
}
if (this._firstActiveTimestamp === 0) {
return;
}
const engagementTime = this.getEngagementTimeSeconds();
const metricData: IMetricTelemetry = {
average: engagementTime,
name: 'React Component Engaged Time (seconds)',
sampleCount: 1
};
const additionalProperties: { [key: string]: any } = { 'Component Name': componentName };
reactPlugin.trackMetric(metricData, additionalProperties);
public constructor(props: P) {
super(props, reactPlugin, componentName);
}
public render() {
@ -92,24 +131,5 @@ export default function withAITracking<P>(reactPlugin: ReactPlugin, Component: R
</div>
);
}
private trackActivity = (e: React.SyntheticEvent<any>): void => {
if (this._firstActiveTimestamp === 0) {
this._firstActiveTimestamp = CoreUtils.dateNow();
this._lastActiveTimestamp = this._firstActiveTimestamp;
} else {
this._lastActiveTimestamp = CoreUtils.dateNow();
}
if (this._idleStartTimestamp > 0) {
const lastIdleTime = this._lastActiveTimestamp - this._idleStartTimestamp;
this._totalIdleTime += lastIdleTime;
this._idleStartTimestamp = 0;
}
}
private getEngagementTimeSeconds(): number {
return (CoreUtils.dateNow() - this._firstActiveTimestamp - this._totalIdleTime - this._idleCount * this._idleTimeout) / 1000;
}
}
}