react-redux-analytics v0.2.1
react-redux-analytics
Analytics middleware for React+Redux To send metrics to tracking server (Google Analytics, Adobe Analytics), use corresponding plugins. (I will release them soon)
Requirements
- React (15.0+)
- Redux
- React-Redux
Features
- Page view tracking with custom variables
- Custom event tracking with custom variables
- Map redux state to custom variables
Installation
npm install --save react-redux-analyticsGetting Started
1. Register Middleware
import { applyMiddleware } from 'redux'
import { analyticsMiddleware } from 'react-redux-analytics'
import { analyticsPluginMiddleware } from 'react-redux-analytics-plugin'
const enhancer = applyMiddleware(...,
analyticsMiddleware({
reducerName: 'analytics',
...
}),
analyticsPluginMiddleware({...})
)2. Register Reducer
import { createStore } from 'redux'
import { analyticsMiddleware } from 'react-redux-analytics'
const store = createStore(combineReducers({
'analytics': analyticsReducer,
...
}), initialState, enhancer);3. Listen location change from history (optional)
import createHistory from 'history/createBrowserHistory'
import { createLocationSubscriber } from 'react-redux-analytics'
const history = createHistory()
const locationSubscriber = createLocationSubscriber(store)
history.listen((location) => {
locationSubscriber.notify(location, "replace")
})4. Track Page View on componentDidMount
import { connect } from 'react-redux'
import { compose } from 'recompose'
import { sendAnalytics } from 'react-redux-analytics'
class NewsPage extends React.Component {
// ...
}
export default compose(
connect({ ... }, { ... }),
sendAnalytics({
pageName: 'news:top',
prop10: '/news',
}),
)(IndexPage)5. Track Custom Event on Click
import { sendEvent } from 'react-redux-analytics'
class NewsPage extends React.Component {
// ...
render(){
const { dispatch } = this.props
return(<div>
...
<div className="button" onClick={()=>{
dispatch(sendEvent({
events: ['event1'],
prop30: 'Click me',
eVar5: 'clicked',
}}>Click me</div>
...
</div>)
}
}API
Higher-order component
sendAnalytics()
SendAnalytics HoC decorates a React component and provides it with the ability to dispatch SEND_PAGE_VIEW action when the component is mounted or updated.
sendAnalytics(options: {
sendPageViewOnDidMount?: boolean | ((props: Object, state: Object) => boolean);
sendPageViewOnDidUpdate?: boolean | (( prevProps: Object, props: Object, state: Object) => boolean);
mapPropsToVariables?: (props: Object, state: Object) => Object;
onDataReady?: boolean | ((props: Object, state: Object) => boolean);
snapshotPropsOnPageView? : boolean | ((props: Object, state: Object) => boolean);
mixins?: string[];
} & { [key:string]: any }) : (wrapped: React.Component) => React.Componentarguments
options (Object)
[sendPageViewOnDidMount](boolean | ((props: Object, state: Object) => boolean)) Iftrue, the decorated component dispatchesSEND_PAGE_VIEWatcomponentDidMount. If the argument is a function, the function is evaluated atcomponentDidMountlifecycle and the action will be dispatched only if the result value istrue. Default value:true[sendPageViewOnDidUpdate](boolean | (( prevProps: Object, props: Object, state: Object) => boolean)) Similar to the abovesendPageViewOnDidMount. The component will dispatchesSEND_PAGE_VIEWatcomponentDidUpdateif the value istrueor the result value of function istrue. Default value:false[mapPropsToVariables]((props: Object, state: Object) => Object) Accepts a function that maps props and redux state to tracking variables when the component dispatchesSEND_PAGE_VIEW. The mapped tracking variables are injected invariablespayload ofSEND_PAGE_VIEW. Default value:undefined[onDataReady](boolean | ((props: Object, state: Object) => boolean)) If a function is specified, the component will delay the dispach ofSEND_PAGE_VIEWuntil the result value of the function becomestrue. The function is first evaluated just aftersendPageViewOnDidMountandsendPageViewOnDidUpdateis evaluated to be true. If the the result of this function istrue, the component dispatches action immediately. Otherwise, the function is evaluated at everycomponentWillReceivePropslifecycle until the result finally becomestrue. (Note: Evaluation of any succeedingsendPageViewOnDidUpdateis skipped if the previousSEND_PAGE_VIEWdispatch is delayed and the result of correspondingonDataReadyevaluation has not yet beentrue) Default value:true[snapshotPropsOnPageView](boolean | ((props: Object, state: Object) => boolean)) Evaluated everytime the component dispatchesSEND_PAGF_VIEW. If the result istrue, it dispatchesPAGE_SNAPSHOT_PROPSto save the props (at the moment) in the page-scope of redux store. (Under[store root]->[analytics scope]->page->snapshot) Default value:false[mixins](string[]) Accpets an array of string to white-list keys of page-scope variables, global variables in the redux store, and global mapped variables. This array is merged withpageviewMixinsinanalyticsMiddleware, forming the concluding white-list. TheanalyticsMiddlwareinjects whilte-listed variables intovariablesofSEND_PAGE_VIEWpayload. Default value:[][staticVariables]({ key: string: any }) The rest properties are regarded as static variables that are injected into everySEND_PAGE_VIEWpayload. Default value:{}
Example
//`/src/components/NewsPageArticle/index.js'
import { connect } from 'react-redux'
import { compose } from 'recompose'
import { sendAnalytics } from 'react-redux-analytics'
import NewsPageArticleComponent from './presentation' // presentational component
export default compose(
connect((state, { index })=>({
loaded: !state.article.loaded,
articleIndex: index,
pageIdx: state.pager.index,
title: state.article.body.title,
author: state.article.body.author,
conent: state.article.body.content,
}),
sendAnalytics({
sendPageViewOnDidMount: true, // always send PageView when component is mounted
sendPageViewOnDidUpdate: (prevProps, props) => prevProps.articleIndex !== props.articleIndex, // send pageView only when readying article is changed. (not when pager is changed)
onDataReady: (props) => props.loaded, // send PageView when the article is loaded from server
mapPropsToVariables: (props) => ({
'prop5': props.title,
'prop10': props.articleIndex,
'eVar9': props.author,
}), // dyanamic variables to be sent with PageView
mixins: ['evar30', 'eVar31'], // variables saved in Redux store and to be sent with PV
pageName: 'ArticlePage', // static variable to send with PV
channel: 'news', // static variable to send with PV
}),
)(NewsPageArticleComponent)Redux Middleware
analyticsMiddleware()
Middleware that transform SEND_PAGE_VIEW and SEND_EVENT action before they reaches to the reducer or plugin middlewares. It extends variables property of the payload of each action by injecting values that come directly from analytics state of redux store, and the values that are mapped from entire redux store. The middleware can also override location property of SEND_PAGE_VIEW payload, and eventName property of SEND_EVENT payload.
analyticsMiddleware(options : {
reducerName?: string;
pageViewMixins?: string[];
eventMixins?: string[];
mapStateToVariables?: (state: Object) => Object;
getLocationInStore?: (state: Object) => Object;
composeEventName?: (composedVariables: Object, state: Object) => string | null;
suppressPageView?: (state: Object, transformedAciton: Object) => boolean,
}) : (store: Redux.Store) => (next: function) => (action: Object) => voidarguments
options (Object)
[reducerName](string) Specifies the name of this middleware's reducer ( that is the same as the name of analytics state in the redux store). This value has to be equal to the alias that you have guven toanalyticsReducerincombineReducersoption. Default value:'analytics'[pageViewMixins](string[]) Accpets an array of string. This array is merged withmixinsinSEND_PAGE_VIEWpayload, forming the final white-list of variables. The middleware selects white-listed variables from analytics state (that are consisted of page-scope variables and global-scope variables) and mapped variable (generated bymapStateToVariables), and then injects them intovariablesofSEND_PAGE_VIEWpayload. Default value:[][eventMixins](string[]) Similar topageViewMixins. This property is merged withmixinsinSEND_EVENTpayload, and is used to extendvariablesproperty ofSEND_EVENTpayload. Default value:[][mapStateToVariables]((state: Object) => Object) Accepts a function that maps the state of entire redux store to variables being used to extendvariablesproperty ofSEND_PAGE_VIEWandSEND_EVENTpayload. Only values whose key are included in the mixin white-list are injected to the payload. Default value:() => ({})[getLocationInStore]((state: Object) => Object) Accepts a function that maps the state of entire redux store to alocationObject that is passed to reducer and plugin middlewares bySEND_PAGE_VIEWaction. The middleware overrides thelocationproperty of the payload with the result of the function only iflocationproperty of the original payload isnulland the return value is NOTnull. Default value:() => null[composeEventName]((composedVariables: Object, state: Object) => string | null) If a function is specified, the middleware use the return value of the function to overrideeventNameproperty ofSEND_EVENTpayload. The function takes two arguments:copmosedVariables(1st argument) that are resultingvariablescomposed of original payloadvariablesand injected values, andstate(2nd argument) that are the state of entire redux store. LikegetLocationInStore, the middleware overrides property only if original property isnulland the output value is NOTnull. Default value:() => null[suppressPageView]((state: Object, transformedAction: Object) => boolean) If a function is specified,analyticsMiddlewareevaluates it just before passing a transfromedSEND_PAGE_VIEWaction to the next middleware. If the return value is false, the action is discarded in this middleare and thus not be received by the plugin middlware (if it is properly placed after this one). This option is only useful if you cannot control dispatches ofSEND_PAGE_VIEWby the lifecycle hooks ofsendAnalytics. Default value:() => false
Example
//'/src/redux/createStore.js'
import { createStore, combineReducers, applyMiddleware } from 'redux'
import {analyticsReducer, analyticsMiddleware} from 'react-redux-analytics'
import { analyticsPluginMiddleware } from 'react-redux-analytics-plugin'
import { routerReducer, routerMiddleware } from 'react-router-redux'
import { articleReducer } from './reducers'
const enhancer = applyMiddleware(
routerMiddleware({
/* ...routerMidlewareOptions, */
}),
analyticsMiddleware({
reducerName: 'analytics', // (1) has to be equalt to the key to specify apnalyticsReducer
pageViewMixins: ['prop40', 'prop41'], // custom variables that are always sent with page view
eventMixins: ['pageName', 'channel'], // custom variables that are always sent with custom events
mapStateToVariables: (state) => ({ //set router state to page view variables
prop40: state.routing.locationBeforeTransitions.pathname,
prop41: state.routing.locationBeforeTransitions.search,
}),
getLocationInStore: (state) => state.routing.locationBeforeTransitions, //use router state instead of window.location
composeEventName: (composedVariables, state) => `${composedVariables}/${composedVariables.events.join(',')}`, //generate eventName automatically from pathname and custom event list
}),
analyticsPluginMiddleware({
/* ...pluginOptions, */
}),
)
export default (initialState = {}) => createStore(combineReducers({
analytics: analyticsReducer, // the key to analyticsReducer has to be the same as 'reducerName' in analyticsMiddleware option
article: articleReducer,
routing: routerReducer
}), initialState, enhancer)Utils
createLocationSubscriber()
Factory of a helper object (LocationSubscriber) that you can use with react-router.
locationSubscriber subscribes changes of location (, which must implement at least [pathname, search, hash] properties of window.location interface) from the routing framework, and changes page state of analytics middlewares in the redux store.
This helper is only useful if you want to utilize this middleware's complex page-scope variables management and historical page stack mechanism.
createLocationSubscriber(options: {
dispatch: (action: Object) => void;
}): { notify: (location: Object, action: 'pop'|'push'|'replace') => void }arguments
options (Object)
dispatch(function) Accepts thedispatchfunction of Redux store.
return
locationSubscriber (Object)
An object that has only one method notify that subscribes changes of location from a routing framework.
notify()If called, one ofLOCATION_PUSH,LOCATION_POP,LOCATION_REPLACEactions is dispatched depending onactionargument of this callback.location(Object) *required Specifies a location object that specifies the location (pathname, search, hash) of next page.Supposed to be informed by the routing framework.[action]('pop'|'push'|'replace') Accepts one of'pop','push', or'replace'. If'pop'is specified, thelocationSubscriberdispatchesLOCATION_POP. And'pop'forLOCATION_PUSH,'replace'forLOCATION_REPLACE.
Example
// '/src/app.js'
import createHistory from 'history/createBrowserHistory'
import { createLocationSubscriber } from 'react-redux-analytics'
import createStore from './redux/createStore' //described in above example
const store = createStore({})
const history = createHistory()
const locationSubscriber = createLocationSubscriber(store);
//set initial location
locationSubscriber.notify(history.location, 'pop')
// Listen for changes to the current location.
const unlisten = history.listen((location, action) => {
locationSubscriber.notify(location, action);
// if you do not need to use variable history, just keep 'replace' the page stack as follows
// locationSubscriber.notify(location, 'replace')
});Action Creators
The following action creators can be used to manually send tracks or to control the middleware's state.
sendPageView()
Creates SEND_PAGE_VIEW action. The payload of SEND_PAGE_VIEW is transformed in analyticsMiddlware, and the transformed action is then relayed to plugin middlewares that finally creates server request to the tracking server. (Adobe analytics, GA, ...)
sendPageView(
location: Object = null,
mixins: string[] = [],
variables: { [key: string]: any } = {},
): Redux.Actionarguments
[location](Object) Accepts awindow.locationlike object (specs is described above). Ifnullis passed, theanalyticsMiddlewaretries to override the property bypage.locationproperty of the state, or the result ofgetLocationInStoreoption of the middleware. (by the respective order) Default value:null[mixins](string[]) Same as themixinproperty of sendAnalyticsoption. If an array of string is specified, it is merged withpageViewMixinsinanalyticsMiddlewareoption to form a white-list of variables to be injected byanalyticsMiddlware. Default value:[][variables]({ key: string: any }) Specifies an object in whichkeyis the name of a variable, and thevalueis the value assigned to the variable. These variables are merged with the injected variables byanalyticsMiddlewareand is sent with a PageView track. Default value:{}
sendEvent()
Creates SEND_EVENT action. Like SEND_PAGE_VIEW, analyticsMiddleware transforms the payload of this action and relays it to plugin middlewares.
sendEvent(
variables: { [key: string]: any} = {},
mixins: string[] = [],
eventName: string = null,
): Redux.Actionarguments
[variables]({ key:string: any}) Specifies variables to be sent withSEND_EVENTin just the same manner assendPageView. The variables are merged with injected ones byanalyticsMiddlewareand sent with an custom event track. (Custom link track in Adobe analytics) Default value:{}[mixins](string[]) Together witheventMixinsinanalyticsMiddleware, this array of string forms a white-list of variables that are injected tovariablespayload of resultingSEND_EVENTaction. Default value:[][eventName](string) An unique name that is used to distinguish or aggregate custom events by the tracking server. If not specified,analyticsMiddlewaretries to override the value by the result ofcomposeEventNamefunction specified at the middleware's option.
pushLocation()
Creates a LOCATION_PUSH action that pushes the current page-scope state to the top of internal page stack, and creates a new page-scope state.
pushLocation(
location: Object,
inherits: (boolean | string[]) = false,
variables: {[key:string]: any} = {}
)arguments
location(Object) *required Specifies awindow.locationlike object. This value is stored in page-scope state of the middleware, and overrides the result ofgetLocationInStoreproperty ofanallyticsMiddlewareoption.[inherits](boolean | string[]) Accepts a boolean value or a string array. If true,variablesproperty of the page-scope state is copied to the new page-scope state. If a string array is specified, only the variables whose name is included in the array are copied. Otherwise, the new page-scope state is created with a blank ({}) variable set. Defaul value:false[variables]({key:string: any}) If specified, the object is used as the initial value ofvariablesproperty of new page-scope state. Ifinheritsis not false, the inherited variables will be merged with this. Default value:{}
popLocation()
Creates a LOCATION_POP action that discards the current page-scope state and restore the one from the top of stack.
popLocation()replaceLocation()
Creates a LOCATION_REPLACE action. This action discards the current page-scope state, and creates a new page-scope state.
replaceLocation(
location: Object,
inherits: (boolean | string[]) = false,
variables: {[key:string]: any} = {}
)arguments
location(Object) *required Same aslocationinpushLocation[inherits](boolean | string[]) Same asinheritsinpushLocation[variables]({key:string: any}) Same asvariablesinpushLocation
updateGlobalVariables()
Creates a GLOBAL_VARIABLES_UPDATE action that sets or updates variables property of global-scope state, which is created with {} value when reducer is initialized, and is persistent over LOCATION_CHANGE, LOCATION_POP, LOCATION_REPLACE actions.
updateGlobalVariables(
variables: {[key: string]: any},
)arguments
variables({key:string: any}) *required Specifies an object to update thevariablesproperty of global-scope state. If a variables with the same key already exists in the state, the value is overriden by the one specified in the argument.
clearGlobalVariables()
Creates a GLOBAL_VARIABLES_CLEAR action, that reset variables property of global-scope state to {}.
clearGlobalVariables()updatePageVariables()
Creates a PAGE_VARIABLES_UPDATE action. Like GLOBAL_VARIABLES_UPDATE, this action sets or updates variables in Redux store. But they are stored in the variables propety of page-scope state, so they will be cleared when any of LOCATION_PUSH, LOCATION_POP, LOCATION_REPLACE actions is dispatched.
updatePageVariables(
variables: {[key: string]: any},
)arguments
variables({key:string: any}) *required Specifies an object to be stored in page-scope state, just likevariablesargument inglobalVariablesUpdate()to page-scope state.
clearPageVariables()
Creates a PAGE_VARIABLES_CLEAR action that resets the variables property of page-scope state. This is the counterpart to clearGlobalVariables(), as updatePageVariables() is to updateGlobalVariables().
clearPageVariables()7 years ago
7 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago