Upgrade to Pro — share decks privately, control downloads, hide ads and more …

react-tracking Declarative event tracking for R...

Avatar for Jeremy Gayed Jeremy Gayed
November 16, 2017

react-tracking Declarative event tracking for React apps

Presentation given at React x Node NYC Meetup about react-tracking open source library, on November 16

Avatar for Jeremy Gayed

Jeremy Gayed

November 16, 2017
Tweet

More Decks by Jeremy Gayed

Other Decks in Technology

Transcript

  1. Jeremy Gayed @tizmagik Lead Software Engineer at The New York

    Times React x Node NYC Meetup, Nov 16 2017 react-tracking
  2. Problem • Permeated the codebase • Inconsistent • No validation

    • Difficult to debug • Providing context was awkward • Impacted performance
  3. Principles • Baked in • Out of the way •

    Context provided naturally • Validation
  4. const logOnRender = msg => Comp => { return class

    Logged extends Component { render() { console.log(msg); return <Comp {...this.props} /> } }; } Quick Aside: Decorators • “Higher-order components” • Function that takes in a component and returns a new (wrapped) component • babel-plugin-transform-decorators-legacy • Can be used via “currying” style @logOnRender('Oh hai!') class Home extends Component { ... }
  5. @track({ appName: 'NYT' }) class App extends Component {...} @track({

    page: 'story' }) class Story extends Component {...} @track({ region: 'header' }) class Header extends Component {...} @track({ region: 'body' }) class Body extends Component {...} @track({ region: 'footer' }) class Footer extends Component {...} @track({ module: 'share-tool' }) class ShareTool extends Component {...} Decorate as you go... <App> <Story> <Header> <ShareTool /> </Header> <Body /> <Footer> <ShareTool /> </Footer> </Story> </App>
  6. Declarative and out of your way @track({ module: 'share-tool' })

    class ShareTool extends Component { @track({ event: 'tweet' }) tweet() { composeTweet() }; render() { return [ <Twitter onClick={this.tweet} /> <Facebook /> <Email /> ]; } } { appName: 'NYT', page: 'story', region: 'header', module: 'share-tool', event: 'tweet' }
  7. Automatic PageView Tracking @track( { appName: 'nyt', }, { process:

    ownTrackingData => ownTrackingData .page ? { event: 'pageview', } : null, } ) class App extends Component {...} @track({ page: 'home' }) class Home extends Component {...} @track({ page: 'story' }) class Story extends Component {...}
  8. Custom Dispatch Behavior @track( { appName: 'nyt', }, { dispatch:

    data => { API.post(data); // anything } } ) class App extends Component {...} // default (GTM) dispatch: data => { (window.dataLayer = window.dataLayer || [] ).push(data); }
  9. Advanced Usage @track((props, state, [event]) => ({ action: 'click', label:

    event.currentTarget.title })) handleClick = (event) => { if (this.props.onClick) { this.props.onClick(event); } } render() { return ( <button onClick={this.handleClick}> {this.props.children} </button> ); }
  10. Imperative Option @track() class Button extends Component { handleClick =

    () => { API.getData().then(result => { if (result.status) { this.props.tracking.trackEvent({ action: 'click', apiResult: result.status, }); } }); }; render() { return <button onClick={this.handleClick}>{this.props.children}</button>; } }
  11. Validation import { validator } from 'tracking-schema' ; export const

    dispatch = data => { validator(data); // logs errors in dev; no-op in prod (window.dataLayer = window.dataLayer || []).push(data); };