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

HOCs API

Recommended Usage

Component TypeRecommendation
Class componentsYou have only the HOC option available.
Atlaskit function componentsUse usePlatformLeafEventHandler.
Other function componentsUse 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 UIAnalyticsEvents.

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 UIAnalyticsEvents, 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;