Open mikegoatly opened 5 years ago
I have used react-document-title in one of my apps with success. I don't see any reason why it shouldn't work with react-helmet either. As long as your page's title in DOM changes within 500ms of the route change, you should be good.
The way I'm using it is like this inside my main Routes.tsx
page that defines all application routes.
And the generateTitle
function looks like this.
private generateTitle(): string {
const { pathname } = this.props;
const baseTitle = localeService.getText("DocumentTitles", "/");
let pathTitle = pathname !== "/" ? localeService.getText("DocumentTitles", pathname) : null;
pathTitle = pathTitle ? ` - ${pathTitle}` : "";
return `${baseTitle}${pathTitle}`;
}
We use a locale service to load a JSON keyvalue pairs of url -> title for the current locale (as my app needs to support multiple country/languages).
When I was working on this feature, I noticed that pageView
registration in AI was recording the new URL but previous page title for me. So I explicitly added 500ms delay to ensure that the AI tracking call received the latest DOM updates.
@mikegoatly I was able to use react-helmet
to accomplish exactly what you're trying to do.
Here is my appinsights.ts
import env from '../environment';
import { reactAI } from 'react-appinsights';
import { ApplicationInsights } from '@microsoft/applicationinsights-web';
import { history } from './history';
class Insights {
private ai: ApplicationInsights;
constructor() {
this.ai = new ApplicationInsights({
config: {
instrumentationKey: `${env.appInsights.instrumentationKey}`,
extensions: [reactAI],
extensionConfig: {
[reactAI.extensionId]: {
debug: true,
history: history,
}
}
}
});
this.ai.loadAppInsights();
}
public setAuthenticatedUserContext = (patientId: string) => {
this.ai.setAuthenticatedUserContext(patientId, undefined, true);
};
public clearAuthenticatedUserContext = () => {
this.ai.clearAuthenticatedUserContext();
};
}
const AppInsightsService = new Insights();
export { AppInsightsService };
the history
object I'm importing from my history.ts
file looks like this:
import { createBrowserHistory } from 'history';
export const history = createBrowserHistory();
It's the same history object I'm importing in index.tsx
and passing to my <Router history={history}
and finally here is the return statement from a simple component where I am using React Helmet
return (
<>
<Helmet>
<title>Forgot Password</title>
</Helmet>
<div>
<span>{this.state.response}</span>
<form ref={this.forgotPasswordForm} onSubmit={this.changePassword}>
<label>
email:
<input type="email" required name="email" value={email} onChange={this.handleChange}/>
</label>
<input type="submit" value="Submit"/>
</form>
<Link to="/login">Login</Link>
</div>
</>
);
Here is a screenshot of what I get back from AppInsights Analytics
The first name is Byte, and is unavoidable I guess because that's what
Thanks both for the responses - it's good to know I was along the right sort of lines.
@hiraldesai I think the problem that I'm running into is that 500ms might not always be enough - I'm using react lazy loading for some routes. On first load the title won't update until the resources are loaded and this would be dependent on network speed.
In a dev environment the chunks take ~1s for me, so it's always a problem on first load. I'm not so worried about this case, but it does highlight the problem.
I'll do a bit more experimentation.
Ok, in order to get this working in a deterministic fashion I've had to remove history tracking and rely on observing changes to the title
DOM element.
This isn't a panacea as it won't handle the case when a history navigation occurs and the resulting page doesn't affect the page title. I can live with that in my application.
I'd love to be able combine the two approaches, i.e. a history navigation occurs, and we then wait for the title to change, and if the title doesn't change after a while then report a navigation anyway, but the problem still stands - I've no idea how long to wait for the lazily loaded components to finish loading. My react-fu isn't strong enough to know if there's a better way involving React Suspense...
In case it's helpful, my wrapper class for AI looks something like this:
class PortalInsights {
private ai: IApplicationInsights | null = null
private titleChangeObserver: MutationObserver;
constructor() {
this.initialize = this.initialize.bind(this);
this.trackPageView = debounce(this.trackPageView, 100).bind(this);
this.titleChangeObserver = new MutationObserver(this.trackPageView);
}
public initialize(user: User) {
const { profile: { email, sub: id } } = user;
var appInsights = new ApplicationInsights({
config: {
instrumentationKey: appInsightsId,
extensions: [reactAI],
extensionConfig: {
[reactAI.extensionId]: {
debug: devenv
}
}
}
});
this.ai = appInsights.loadAppInsights();
appInsights.setAuthenticatedUserContext(id.toString());
const titleNode = document.querySelector('title');
if (titleNode != null) {
this.titleChangeObserver.observe(
titleNode,
{ childList: true, characterData: true, subtree: true }
);
}
}
public trackPageView() {
const pathname = window.location.pathname;
const name = document.title;
if (name != null && name.length > 0 && this.ai == null) {
this.ai.trackPageView({ uri: pathname, name });
}
}
...
}
Where the debounce
helper function is used to prevent rapid successive changes to title causing suprious page change notifications:
function debounce(func: () => void, wait: number) {
let timeout: NodeJS.Timeout | null;
return function (this: any) {
const context: any = this;
const args: any = arguments;
if (timeout) {
clearTimeout(timeout);
}
timeout = setTimeout(() => {
timeout = null;
func.apply(context, args);
}, wait);
};
};
Version: 3.0.0-rc.6
I noticed that every page view was being tracked with the same page name - it didn't take long to realise it was because I wasn't changing the
I'm using react-router and that doesn't seem to have a mechanism to set the page title.
I've tried to use react-helmet to set the title when a component is loaded, but it seems this is too late and the page title has already been captured to send with the telemetry.
Has anyone else come up with an approach that will allow for the page title to be updated when a route changes and be captured correctly in page view analytics?