react-tracker
Advanced tools
+247
| #  | ||
| # react-tracker [](https://badge.fury.io/js/react-tracker) | ||
| - React specific tracking library, usable as a higher-order component | ||
| - Flexible-scalable solution for tracking in React Apps | ||
| - Easy to use (Redux-like) | ||
| - Can be pluged with any Analytics platform agnostic lib (You can mainly do anything in the event listeners callback) | ||
| ## Installation | ||
| ``` | ||
| npm install --save react-tracker | ||
| ``` | ||
| This assumes you are using [npm](https://www.npmjs.com/) as your package manager. | ||
| ## The Gist | ||
| The idea is to only provide one Tracker! | ||
| Why? because at some you'll need to apply some restriction on some events E.g. : | ||
| - Track product click only once! | ||
| - Track product click only if page views is already tracked | ||
| in these case we need to have the tracking history.. that's why we keep the history synced by providing the **same instance of tracker to all components** | ||
| ```js | ||
| import { Tracker } from 'react-tracker'; | ||
| /** | ||
| * This is an event listner, a pure function with (event, eventsHistory) => tracking goes here. | ||
| * It describes what to do with the just-fired event. | ||
| * | ||
| * There is two types of event listeners | ||
| * 1- Listener with eventType specified it will be called when the given eventType is fired/dispatched | ||
| * 2- Listener with no eventType it will be called whenevent an event fired/dispatched | ||
| * Think of the last type of listeners as `jQuery.on('*', callback)` | ||
| * You can use `switch` statement to handle multiple events in one listener | ||
| */ | ||
| // Listener-per-event example | ||
| function trackAddToCartClick(event = {}, eventsHistory) { | ||
| if (event.type) { | ||
| // Call GTM or you tracking provider... | ||
| // If you want save this event, just return it, otherwise it will be ignored. | ||
| return event | ||
| } | ||
| } | ||
| // Allow `trackAddToCartClick` to listen only on `ADD_TO_CART_BUTTON_CLICK` event! | ||
| trackAddToCartClick.eventType = 'ADD_TO_CART_BUTTON_CLICK'; | ||
| // Listen-on-all example | ||
| function trackCartEvents(event = {}, eventsHistory) { | ||
| switch(event.type) { | ||
| case 'ADD_TO_CART_BUTTON_CLICK': | ||
| // CALL your tracking provider.. | ||
| // If you want save this event, just return it, otherwise it will be ignored. | ||
| return event | ||
| case 'REMOVE_FROM_CART_CLICK': | ||
| // Your tracking logic goes here.. | ||
| break; | ||
| default: | ||
| // silence | ||
| } | ||
| // Create a Tracker holding the tracked events History of your app. | ||
| // Its API is { on, trackEvent, getHistory }. | ||
| let tracker = new Tracker([trackAddToCartClick, trackCartEvents]) | ||
| // In additional to to intialize the tracker with event listeners. | ||
| // You can add a an event listener using `on()` | ||
| // Listen on `PRODUCT_CLICK`events. | ||
| tracker.on('PRODUCT_CLICK', (event, eventsHistory) => | ||
| console.log(event) | ||
| ); | ||
| // Listen on all events | ||
| tracker.on('*', (event, eventsHistory) => | ||
| console.log(event) | ||
| ); | ||
| // And then you can fire an events using `trackEvent` function : | ||
| // Fire `ADD_TO_CART_EVENT` | ||
| tracker.trackEvent({ type: 'ADD_TO_CART_EVENT' }) | ||
| // Fire `ADD_TO_CART_EVENT` | ||
| tracker.trackEvent({ type: 'ADD_TO_CART_EVENT' }) | ||
| // Fire `PRODUCT_CLICK` | ||
| tracker.trackEvent({ type: 'PRODUCT_CLICK' }) | ||
| ``` | ||
| ## Usage with React | ||
| #### `components/Product.js` | ||
| ```js | ||
| import React from 'react' | ||
| import PropTypes from 'prop-types' | ||
| const Product = ({ onClick, title, price, currency }) => ( | ||
| <li | ||
| onClick={onClick} | ||
| > | ||
| {title} | ||
| <span> {price} {currency} </span> | ||
| </li> | ||
| ) | ||
| Product.propTypes = { | ||
| onClick: PropTypes.func.isRequired, | ||
| title: PropTypes.bool.isRequired, | ||
| price: PropTypes.string.isRequired | ||
| currency: PropTypes.string.isRequired | ||
| } | ||
| export default Product | ||
| ``` | ||
| #### `components/ProductList.js` | ||
| ```js | ||
| import React from 'react' | ||
| import PropTypes from 'prop-types' | ||
| import Product from './Product' | ||
| const ProductList = ({ products, trackProductClick }) => ( | ||
| <ul> | ||
| {products.map(product => ( | ||
| <Product key={product.id} {...product} onClick={() => trackProductClick(product.id, product.price)} /> | ||
| ))} | ||
| </ul> | ||
| ) | ||
| ProductList.propTypes = { | ||
| products: PropTypes.arrayOf( | ||
| PropTypes.shape({ | ||
| id: PropTypes.number.isRequired, | ||
| title: PropTypes.string.isRequired, | ||
| price: PropTypes.string.isRequired, | ||
| currency: PropTypes.string.isRequired, | ||
| }).isRequired | ||
| ).isRequired, | ||
| trackProductClick: PropTypes.func.isRequired | ||
| } | ||
| export default ProductList | ||
| ``` | ||
| ### Implementing Container Components | ||
| Now it's tracking time.. | ||
| To use `withTracking()`, you need to define a special function (react-redux-like) called `mapTrackingToProps` that tells how to pass the trackEvent function to the props. | ||
| For example, we need to track product click with Product ID and price, so we need to fire the event on the procut click! | ||
| !!!! as we know the `trackEvent` only accpect the object that describes the Event (type and data if any) !!!! | ||
| #### `.../tracking/listeners/cart.js` | ||
| ```js | ||
| function trackProductClick(event = {}, eventsHistory) { | ||
| if (event.type) { | ||
| // Call GTM or you tracking provider... | ||
| // If you want save this event, just return it, otherwise it will be ignored. | ||
| return event | ||
| } | ||
| } | ||
| // scope `trackProductClick` to listen only on `PRODUCT_CLICK` event! | ||
| trackProductClick.eventType = 'PRODUCT_CLICK'; | ||
| ``` | ||
| #### `.../tracking/events/cart.js` | ||
| ```js | ||
| function getProductClickEvent(id, price) { | ||
| return { | ||
| type: 'PRODUCT_CLICK', | ||
| data: { | ||
| id: id, | ||
| price: price | ||
| } | ||
| } | ||
| }; | ||
| ``` | ||
| ```js | ||
| const mapTrackingToProps = trackEvent => { | ||
| return { | ||
| trackProductClick: (id, price) => { | ||
| trackEvent(getProductClickEvent(id, price)) | ||
| } | ||
| } | ||
| } | ||
| ``` | ||
| Finally, we create the `ProductsList` by calling `withTracking()` and passing our `mapTrackingToProps`: | ||
| ```js | ||
| import { withTracking } from 'react-tracker' | ||
| const ProductsListWithTracking = withTracking(mapDispatchToProps)(ProductsList) | ||
| export default ProductsListWithTracking | ||
| ``` | ||
| ## Let React meet our Tracker | ||
| All container components need access to the tracker so they can fire events. | ||
| We will use the `<Provider>` to [magically](https://facebook.github.io/react/docs/context.html) make the tracker available to all container components in the application without passing it explicitly. | ||
| You only need to use it once when you render the root component: | ||
| #### `index.js` | ||
| ```js | ||
| import React from 'react' | ||
| import { render } from 'react-dom' | ||
| import { TrackerProvider, Tracker } from 'react-tracker' | ||
| import { trackProductClick } from './tracking/listeners/cart' | ||
| import ProductsList from './components/ProductsList' | ||
| let store = new Tracker([trackProductClick /*, other event listeners goes here*/]) | ||
| render( | ||
| <TrackerProvider tracker={store}> | ||
| <ProductsList products={someProducts} /> | ||
| </TrackerProvider>, | ||
| document.getElementById('root') | ||
| ) | ||
| ``` | ||
| ## Contribution | ||
| This project is in its early stages, I'd be very happy if you can help :) | ||
| ## License | ||
| MIT |
+1
-1
| { | ||
| "name": "react-tracker", | ||
| "version": "0.0.3", | ||
| "version": "0.0.4", | ||
| "description": "Track actions within your React app with a tracker redux-like!", | ||
@@ -5,0 +5,0 @@ "main": "build/index.js", |
| !function(e,t){"object"==typeof exports&&"object"==typeof module?module.exports=t():"function"==typeof define&&define.amd?define([],t):"object"==typeof exports?exports.ReactTracker=t():e.ReactTracker=t()}("undefined"!=typeof self?self:this,function(){return function(e){function t(n){if(r[n])return r[n].exports;var o=r[n]={i:n,l:!1,exports:{}};return e[n].call(o.exports,o,o.exports,t),o.l=!0,o.exports}var r={};return t.m=e,t.c=r,t.d=function(e,r,n){t.o(e,r)||Object.defineProperty(e,r,{configurable:!1,enumerable:!0,get:n})},t.n=function(e){var r=e&&e.__esModule?function(){return e.default}:function(){return e};return t.d(r,"a",r),r},t.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},t.p="build/",t(t.s=4)}([function(e,t,r){"use strict";e.exports=r(6)},function(e,t,r){"use strict";function n(e){return function(){return e}}var o=function(){};o.thatReturns=n,o.thatReturnsFalse=n(!1),o.thatReturnsTrue=n(!0),o.thatReturnsNull=n(null),o.thatReturnsThis=function(){return this},o.thatReturnsArgument=function(e){return e},e.exports=o},function(e,t,r){"use strict";var n=r(9),o=r.n(n);t.a=(Object.assign||function(e){for(var t=1;arguments.length>t;t++){var r=arguments[t];for(var n in r)Object.prototype.hasOwnProperty.call(r,n)&&(e[n]=r[n])}return e})({},o.a,{tracker:o.a.shape({subscribe:o.a.func.isRequired,dispatch:o.a.func.isRequired})})},function(e,t,r){"use strict";r.d(t,"b",function(){return n}),r.d(t,"a",function(){return o});var n=function(e){return"TrackEventProvider("+(e.displayName||e.name||"Component")+")"},o=function(){return(arguments.length>0&&void 0!==arguments[0]?arguments[0]:[]).map(function(e){var t=Object.keys(e)[0];if(t&&"function"==typeof e[t])return e[t].eventType=t,e[t]}).filter(function(e){return!!e})}},function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var n=r(5),o=r(13),i=r(14);r.d(t,"Tracker",function(){return i.a}),r.d(t,"TrackerProvider",function(){return n.a}),r.d(t,"provideTrackEvent",function(){return o.a})},function(e,t,r){"use strict";function n(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}function o(e,t){if(!e)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return!t||"object"!=typeof t&&"function"!=typeof t?e:t}function i(e,t){if("function"!=typeof t&&null!==t)throw new TypeError("Super expression must either be null or a function, not "+typeof t);e.prototype=Object.create(t&&t.prototype,{constructor:{value:e,enumerable:!1,writable:!0,configurable:!0}}),t&&(Object.setPrototypeOf?Object.setPrototypeOf(e,t):e.__proto__=t)}var u=r(0),c=(r.n(u),r(2)),a=function(){function e(e,t){for(var r=0;t.length>r;r++){var n=t[r];n.enumerable=n.enumerable||!1,n.configurable=!0,"value"in n&&(n.writable=!0),Object.defineProperty(e,n.key,n)}}return function(t,r,n){return r&&e(t.prototype,r),n&&e(t,n),t}}(),f=function(e){function t(e,r){n(this,t);var i=o(this,(t.__proto__||Object.getPrototypeOf(t)).call(this,e,r));return i.tracker=e.tracker,i}return i(t,e),a(t,[{key:"getChildContext",value:function(){return{trackEvent:this.tracker.dispatch}}}]),a(t,[{key:"render",value:function(){return u.Children.only(this.props.children)}}]),t}(u.Component);t.a=f,f.propTypes={children:c.a.element.isRequired},f.childContextTypes={trackEvent:c.a.func.isRequired}},function(e,t,r){"use strict";function n(e){for(var t=arguments.length-1,r="Minified React error #"+e+"; visit http://facebook.github.io/react/docs/error-decoder.html?invariant="+e,n=0;t>n;n++)r+="&args[]="+encodeURIComponent(arguments[n+1]);throw t=Error(r+" for the full message or use the non-minified dev environment for full errors and additional helpful warnings."),t.name="Invariant Violation",t.framesToPop=1,t}function o(e,t,r){this.props=e,this.context=t,this.refs=g,this.updater=r||R}function i(e,t,r){this.props=e,this.context=t,this.refs=g,this.updater=r||R}function u(){}function c(e,t,r){this.props=e,this.context=t,this.refs=g,this.updater=r||R}function a(e,t,r){var n,o={},i=null,u=null;if(null!=t)for(n in void 0!==t.ref&&(u=t.ref),void 0!==t.key&&(i=""+t.key),t)A.call(t,n)&&!q.hasOwnProperty(n)&&(o[n]=t[n]);var c=arguments.length-2;if(1===c)o.children=r;else if(c>1){for(var a=Array(c),f=0;c>f;f++)a[f]=arguments[f+2];o.children=a}if(e&&e.defaultProps)for(n in c=e.defaultProps)void 0===o[n]&&(o[n]=c[n]);return{$$typeof:j,type:e,key:i,ref:u,props:o,_owner:C.current}}function f(e){return"object"==typeof e&&null!==e&&e.$$typeof===j}function s(e){var t={"=":"=0",":":"=2"};return"$"+(""+e).replace(/[=:]/g,function(e){return t[e]})}function l(e,t,r,n){if(I.length){var o=I.pop();return o.result=e,o.keyPrefix=t,o.func=r,o.context=n,o.count=0,o}return{result:e,keyPrefix:t,func:r,context:n,count:0}}function p(e){e.result=null,e.keyPrefix=null,e.func=null,e.context=null,e.count=0,10>I.length&&I.push(e)}function y(e,t,r,o){var i=typeof e;"undefined"!==i&&"boolean"!==i||(e=null);var u=!1;if(null===e)u=!0;else switch(i){case"string":case"number":u=!0;break;case"object":switch(e.$$typeof){case j:case _:case w:case x:u=!0}}if(u)return r(o,e,""===t?"."+h(e,0):t),1;if(u=0,t=""===t?".":t+":",Array.isArray(e))for(var c=0;e.length>c;c++){i=e[c];var a=t+h(i,c);u+=y(i,a,r,o)}else if(null===e||void 0===e?a=null:(a=E&&e[E]||e["@@iterator"],a="function"==typeof a?a:null),"function"==typeof a)for(e=a.call(e),c=0;!(i=e.next()).done;)i=i.value,a=t+h(i,c++),u+=y(i,a,r,o);else"object"===i&&(r=""+e,n("31","[object Object]"===r?"object with keys {"+Object.keys(e).join(", ")+"}":r,""));return u}function h(e,t){return"object"==typeof e&&null!==e&&null!=e.key?s(e.key):t.toString(36)}function d(e,t){e.func.call(e.context,t,e.count++)}function b(e,t,r){var n=e.result,o=e.keyPrefix;e=e.func.call(e.context,t,e.count++),Array.isArray(e)?v(e,n,r,k.thatReturnsArgument):null!=e&&(f(e)&&(t=o+(!e.key||t&&t.key===e.key?"":(""+e.key).replace($,"$&/")+"/")+r,e={$$typeof:j,type:e.type,key:t,ref:e.ref,props:e.props,_owner:e._owner}),n.push(e))}function v(e,t,r,n,o){var i="";null!=r&&(i=(""+r).replace($,"$&/")+"/"),t=l(t,i,n,o),null==e||y(e,"",b,t),p(t)}var m=r(7),g=r(8),k=r(1),O="function"==typeof Symbol&&Symbol.for,j=O?Symbol.for("react.element"):60103,_=O?Symbol.for("react.call"):60104,w=O?Symbol.for("react.return"):60105,x=O?Symbol.for("react.portal"):60106,P=O?Symbol.for("react.fragment"):60107,E="function"==typeof Symbol&&Symbol.iterator,R={isMounted:function(){return!1},enqueueForceUpdate:function(){},enqueueReplaceState:function(){},enqueueSetState:function(){}};o.prototype.isReactComponent={},o.prototype.setState=function(e,t){"object"!=typeof e&&"function"!=typeof e&&null!=e&&n("85"),this.updater.enqueueSetState(this,e,t,"setState")},o.prototype.forceUpdate=function(e){this.updater.enqueueForceUpdate(this,e,"forceUpdate")},u.prototype=o.prototype;var T=i.prototype=new u;T.constructor=i,m(T,o.prototype),T.isPureReactComponent=!0;var S=c.prototype=new u;S.constructor=c,m(S,o.prototype),S.unstable_isAsyncReactComponent=!0,S.render=function(){return this.props.children};var C={current:null},A=Object.prototype.hasOwnProperty,q={key:!0,ref:!0,__self:!0,__source:!0},$=/\/+/g,I=[],N={Children:{map:function(e,t,r){if(null==e)return e;var n=[];return v(e,n,null,t,r),n},forEach:function(e,t,r){if(null==e)return e;t=l(null,null,t,r),null==e||y(e,"",d,t),p(t)},count:function(e){return null==e?0:y(e,"",k.thatReturnsNull,null)},toArray:function(e){var t=[];return v(e,t,null,k.thatReturnsArgument),t},only:function(e){return f(e)||n("143"),e}},Component:o,PureComponent:i,unstable_AsyncComponent:c,Fragment:P,createElement:a,cloneElement:function(e,t,r){var n=m({},e.props),o=e.key,i=e.ref,u=e._owner;if(null!=t){if(void 0!==t.ref&&(i=t.ref,u=C.current),void 0!==t.key&&(o=""+t.key),e.type&&e.type.defaultProps)var c=e.type.defaultProps;for(a in t)A.call(t,a)&&!q.hasOwnProperty(a)&&(n[a]=void 0===t[a]&&void 0!==c?c[a]:t[a])}var a=arguments.length-2;if(1===a)n.children=r;else if(a>1){c=Array(a);for(var f=0;a>f;f++)c[f]=arguments[f+2];n.children=c}return{$$typeof:j,type:e.type,key:o,ref:i,props:n,_owner:u}},createFactory:function(e){var t=a.bind(null,e);return t.type=e,t},isValidElement:f,version:"16.2.0",__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED:{ReactCurrentOwner:C,assign:m}},U=Object.freeze({default:N}),F=U&&N||U;e.exports=F.default?F.default:F},function(e,t,r){"use strict";function n(e){if(null===e||void 0===e)throw new TypeError("Object.assign cannot be called with null or undefined");return Object(e)}var o=Object.getOwnPropertySymbols,i=Object.prototype.hasOwnProperty,u=Object.prototype.propertyIsEnumerable;e.exports=function(){try{if(!Object.assign)return!1;var e=new String("abc");if(e[5]="de","5"===Object.getOwnPropertyNames(e)[0])return!1;for(var t={},r=0;10>r;r++)t["_"+String.fromCharCode(r)]=r;if("0123456789"!==Object.getOwnPropertyNames(t).map(function(e){return t[e]}).join(""))return!1;var n={};return"abcdefghijklmnopqrst".split("").forEach(function(e){n[e]=e}),"abcdefghijklmnopqrst"===Object.keys(Object.assign({},n)).join("")}catch(e){return!1}}()?Object.assign:function(e,t){for(var r,c,a=n(e),f=1;arguments.length>f;f++){r=Object(arguments[f]);for(var s in r)i.call(r,s)&&(a[s]=r[s]);if(o){c=o(r);for(var l=0;c.length>l;l++)u.call(r,c[l])&&(a[c[l]]=r[c[l]])}}return a}},function(e,t,r){"use strict";var n={};e.exports=n},function(e,t,r){e.exports=r(10)()},function(e,t,r){"use strict";var n=r(1),o=r(11),i=r(12);e.exports=function(){function e(e,t,r,n,u,c){c!==i&&o(!1,"Calling PropTypes validators directly is not supported by the `prop-types` package. Use PropTypes.checkPropTypes() to call them. Read more at http://fb.me/use-check-prop-types")}function t(){return e}e.isRequired=e;var r={array:e,bool:e,func:e,number:e,object:e,string:e,symbol:e,any:e,arrayOf:t,element:e,instanceOf:t,node:e,objectOf:t,oneOf:t,oneOfType:t,shape:t,exact:t};return r.checkPropTypes=n,r.PropTypes=r,r}},function(e,t,r){"use strict";function n(e,t,r,n,i,u,c,a){if(o(t),!e){var f;if(void 0===t)f=Error("Minified exception occurred; use the non-minified dev environment for the full error message and additional helpful warnings.");else{var s=[r,n,i,u,c,a],l=0;f=Error(t.replace(/%s/g,function(){return s[l++]})),f.name="Invariant Violation"}throw f.framesToPop=1,f}}var o=function(e){};e.exports=n},function(e,t,r){"use strict";e.exports="SECRET_DO_NOT_PASS_THIS_OR_YOU_WILL_BE_FIRED"},function(e,t,r){"use strict";var n=r(0),o=r.n(n),i=r(3),u=r(2),c=Object.assign||function(e){for(var t=1;arguments.length>t;t++){var r=arguments[t];for(var n in r)Object.prototype.hasOwnProperty.call(r,n)&&(e[n]=r[n])}return e};t.a=function(e,t){var r=Object(i.b)(e),n=function(t,n){var i=n.trackEvent;if(!i)throw Error('Could not find tracker in the context of "'+r+'"');var u=c({},t,{trackEvent:i});return o.a.createElement(e,u)};return n.displayName=r,n.contextTypes={trackEvent:u.a.func.isRequired},n}},function(e,t,r){"use strict";function n(e){if(Array.isArray(e)){for(var t=0,r=Array(e.length);e.length>t;t++)r[t]=e[t];return r}return Array.from(e)}function o(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}var i=r(3),u=function(){function e(e,t){for(var r=0;t.length>r;r++){var n=t[r];n.enumerable=n.enumerable||!1,n.configurable=!0,"value"in n&&(n.writable=!0),Object.defineProperty(e,n.key,n)}}return function(t,r,n){return r&&e(t.prototype,r),n&&e(t,n),t}}();t.a=function(){function e(t){o(this,e),this.trackingHistory=[],this.subscribers=Object(i.a)(t),this.dispatch=this.dispatch.bind(this)}return u(e,[{key:"on",value:function(e,t){"function"==typeof t&&e&&"string"==typeof e&&(t.eventType=e,this.subscribers=[].concat(n(this.subscribers),[t]))}},{key:"dispatch",value:function(e){var t=e||{},r=t.type;if(!r)return null;for(var o=0;this.subscribers.length>o;o++)if("function"==typeof this.subscribers[o]&&this.subscribers[o].eventType===r){var i=this.subscribers[o].call(null,e,this.trackingHistory);i&&(this.trackingHistory=[].concat(n(this.trackingHistory),[i]))}}},{key:"getHistory",value:function(){return this.trackingHistory}}]),e}()}])}); |
No README
QualityPackage does not have a README. This may indicate a failed publish or a low quality package.
Found 1 instance in 1 package
Minified code
QualityThis package contains minified code. This may be harmless in some cases where minified code is included in packaged libraries, however packages on npm should not minify code.
Found 1 instance in 1 package
URL strings
Supply chain riskPackage contains fragments of external URLs or IP addresses, which the package may be accessing at runtime.
Found 1 instance in 1 package
1
-50%1
-50%248
Infinity%0
-100%8214
-37.31%