Analytics next - Usage with presentational components
This section will guide how to add analytics tracking to presentational and other components that don't fit into the "Container" category.
For a conceptual overview of @atlaskit/analytics-next
, please consult the
concepts page.
There are several ways to add analytics to a component, via React hooks, or via higher order components (HOCs) - consult the Recommended Usage table for our suggested approach.
API
Hooks API
useAnalyticsEvents
useCallbackWithAnalytics
usePlatformLeafEventHandler
andusePlatformLeafSyntheticEventHandler
HOCs API
Recommended Usage
Component Type | Recommendation |
---|---|
Class components | You have only the HOC option available. |
Atlaskit function components | Use usePlatformLeafEventHandler . |
Other function components | Use useCallbackWithAnalytics if you want something basic.Use useAnalyticsEvents if you want more control. |
Examples
The useAnalyticsEvents
hook
This custom React hook provides a method createAnalyticsEvent
for creating UIAnalyticsEvent
s.
import React, { useCallback } from 'react';
import { useAnalyticsEvents } from '@atlaskit/analytics-next';
const MyButton = ({ onClick = () => {} }) => {
const { createAnalyticsEvent } = useAnalyticsEvents();
const handler = useCallback((clickEvent) => {
const analyticsEvent = createAnalyticsEvent({
action: 'clicked',
componentName: 'my-button',
packageName: '@atlaskit/my-button',
packageVersion: '1.0.0'
});
// do what you like with the event, fire / modify / clone
// this here is the typical usage pattern for atlaskit components
const clonedEvent = analyticsEvent.clone();
analyticsEvent.fire("atlaskit");
// firing is prevented from happening more than once
// so that's why we pass a clone to be accessed by the parent component
// which might also want to fire their own event on this ui interaction
onClick(clickEvent, clonedEvent);
}, [ createAnalyticsEvent, onClick ]);
return (
<button onClick={handler}>Track me</button>
);
}
export MyButton;
The useCallbackWithAnalytics
hook
This custom React hook takes a callback function and an event payload, and channel, and returns a callback to fire the event and call the provided function.
The hooks stores the input and memoizes the return value to optimize performance.
import React, { useCallback } from 'react';
import { useCallbackWithAnalytics } from '@atlaskit/analytics-next';
const MyButton = ({ onClick = () => {} }) => {
const handler = useCallbackWithAnalytics(onClick, {
action: 'clicked',
componentName: 'my-button',
packageName: '@atlaskit/my-button',
packageVersion: '1.0.0'
}, 'atlaskit');
return (
<button onClick={handler}>Track me</button>
);
}
export MyButton;
The withAnalyticsEvents
HOC
A HOC which provides the wrapped component with a method for creating UIAnalyticsEvent
s,
via props.createAnalyticsEvent
.
The HOC supports a few ways to use it for convinvience.
The first is to use the createAnalyticsEvent
prop the HOC passes to the component manually:
import { withAnalyticsEvents } from '@atlaskit/analytics-next';
const MyButton = ({ onClick = () => {}, createAnalyticsEvent }) => {
const handler = (clickEvent) => {
const analyticsEvent = createAnalyticsEvent({
action: 'clicked',
componentName: 'my-button',
packageName: '@atlaskit/my-button',
packageVersion: '1.0.0'
});
// do what you like with the event, fire / modify / clone
// this here is the typical usage pattern for atlaskit components
const clonedEvent = analyticsEvent.clone();
analyticsEvent.fire("atlaskit");
// firing is prevented from happening more than once
// so that's why we pass a clone to be accessed by the parent component
// which might also want to fire their own event on this ui interaction
onClick(clickEvent, clonedEvent);
};
return (
<button onClick={handler}>Track me</button>
);
}
const AnalyticsWrappedButton = withAnalyticsEvents()(MyButton);
export AnalyticsWrappedButton;
Altenatively, the HOC accepts an optional map as its first argument, which provides a shortcut for passing a new analytics event as an additional parameter to the corresponding callback prop.
import { withAnalyticsEvents } from '@atlaskit/analytics-next';
const MyButton = ({ onClick = () => {} }) => {
const handler = (clickEvent, analyticsEvent) => {
// do what you like with the event, fire / modify / clone
// this here is the typical usage pattern for atlaskit components
const clonedEvent = analyticsEvent.clone();
analyticsEvent.fire("atlaskit");
// firing is prevented from happening more than once
// so that's why we pass a clone to be accessed by the parent component
// which might also want to fire their own event on this ui interaction
onClick(clickEvent, clonedEvent);
};
return (
<button onClick={handler}>Track me</button>
);
}
const AnalyticsWrappedButton = withAnalyticsEvents({
onClick: (createAnalyticsEvent) => createAnalyticsEvent({
action: 'clicked',
componentName: 'my-button',
packageName: '@atlaskit/my-button',
packageVersion: '1.0.0'
})
})(MyButton);
export AnalyticsWrappedButton;
Since creating and returning an event is such a common pattern an even more concise shorthand is supported:
import { withAnalyticsEvents } from '@atlaskit/analytics-next';
const MyButton = ({ onClick = () => {} }) => {
const handler = (clickEvent, analyticsEvent) => {
// do what you like with the event, fire / modify / clone
// this here is the typical usage pattern for atlaskit components
const clonedEvent = analyticsEvent.clone();
analyticsEvent.fire("atlaskit");
// firing is prevented from happening more than once
// so that's why we pass a clone to be accessed by the parent component
// which might also want to fire their own event on this ui interaction
onClick(clickEvent, clonedEvent);
};
return (
<button onClick={handler}>Track me</button>
);
}
const AnalyticsWrappedButton = withAnalyticsEvents({
onClick: {
action: 'clicked',
componentName: 'my-button',
packageName: '@atlaskit/my-button',
packageVersion: '1.0.0'
}
})(MyButton);
export AnalyticsWrappedButton;
The usePlatformLeafEventHandler
and usePlatformLeafSyntheticEventHandler
hooks
These hooks were built with internal leaf node components purely in mind.
They dispatch an event on the atlaskit
channel, then pass it to the
wrapped function as the last argument in case you want to something additional with the event.
usePlatformLeafEventHandler
takes a function with two arguments (value
and
analyticsEvent
), while usePlatformLeafSyntheticEventHandler
takes a function
with just one argument for the analyticsEvent
WARNING: These hooks make an assumption that the component being wrapped is a "leaf-node" component, i.e., they have no children that require analytics themselves. This is so it can include the component name, package name, and package version as context in the analytics event. It assumes no children need this context and makes no attempt to pass it to them. This gains us a decent performance optimization. Use the other hooks if you are unsure you can use these safely.
import React, { useCallback } from 'react';
import {
usePlatformLeafEventHandler,
usePlatformLeafSyntheticEventHandler
} from '@atlaskit/analytics-next';
const MyButton = ({ onClick = () => {}, onActivate = () => {}}) => {
/**
* Event 1: usePlatformLeafEventHandler
*/
// this isn't necessary if you are happy with just firing on the 'atlaskit' channel
const wrapped = (clickEvent, analyticsEvent) => {
// do what you like with the event, fire / modify / clone
// this here is the typical usage pattern for atlaskit components
const clonedEvent = analyticsEvent.clone();
analyticsEvent.fire("otherChannel");
// firing is prevented from happening more than once
// so that's why we pass a clone to be accessed by the parent component
// which might also want to fire their own event on this ui interaction
onClick(clickEvent, clonedEvent);
};
const handler = usePlatformLeafEventHandler({
fn: wrapped, // use onClick instead if you want to just fire on 'atlaskit'
action: 'clicked',
componentName: 'my-button',
actionSubject : 'clickButton', // will use componentName as fallback if actionSubject is not passed
packageName: '@atlaskit/my-button',
packageVersion: '1.0.0',
analyticsData: {
// any additional data can live here
style: 'fancy'
},
});
/**
* Event 2: Synthetic event using usePlatformLeafSyntheticEventHandler
*/
const wrappedSynthetic = (analyticsEvent) => {
// A synthetic 'activate' event; same as above, but without a DOM event passed in
const clonedEvent = analyticsEvent.clone();
analyticsEvent.fire("otherChannel");
onActivate(clonedEvent);
};
const syntheticHandler = usePlatformLeafSyntheticEventHandler({
fn: wrappedSynthetic, // use onActivate instead if you want to just fire on 'atlaskit'
action: 'activated',
componentName: 'my-button',
packageName: '@atlaskit/my-button',
packageVersion: '1.0.0',
analyticsData: {
// any additional data can live here
style: 'fancy'
},
});
return (
// In this example the synthetic event is triggered by onFocus
<button onClick={handler} onFocus={syntheticHandler}>Track me</button>
);
}
export MyButton;