nytimes / react-tracking

🎯 Declarative tracking for React apps.
https://open.nytimes.com/introducing-react-tracking-declarative-tracking-for-react-apps-2c76706bb79a
Other
1.88k stars 123 forks source link

Duplicated data entries and parent components challenges #85

Closed daniel-winter closed 5 years ago

daniel-winter commented 6 years ago

Dear NYTimes team, dear @tizmagik,

we are currently working on a relaunch of our online-shop/platform with ReactJs. For tracking purposes we want to use your powerful, light and well implemented plugin. Before we go online, we have a few challenges to overcome. Therefore, we would be very happy if you could share your experiences with us:

Specifically, since ReactJs's lifecycle calls methods more than only once to varify if there are dom-changes, the tracking decorator is triggered more than once too. This results in duplicated entries on the dataLayer. Do you have any solution for this? Or do get the duplicate data filtered by you data analyst? Do get the data filtered by comparing milliseconds in timestamps?

The second challenge for which we do not yet have a solution: Tracking parent components. For a better explanation here is a short example: We display multiple products on one page grouped by sliders named "recommendations" and "other-users-liked". It may happen that one product appears in both sliders at the same time. A click on the product then triggers a tracking action. Because we have component based architecture, the product component doesn't know anything from the parent slider. But we do need to get this information in which slider the product has been to ensure proper analytics on which slider is more successful. Did you have similar problems? If so, how did you solve this?

We would be very happy and grateful if you could share your experiences with us.

Best wishes, Daniel

tizmagik commented 6 years ago

Hello @daniel-winter ! It's great to hear you're interested in using react-tracking for your project. I think there are good solutions to both problems you've described.

Duplicate Data

We have not had this issue as we only dispatch tracking events on "one-time" events, such as click handlers and in componentDidMount(). In both cases, we want to know every time they happen. For example, we decorate our Page components with a "page" tracking property, and define a top-level process() function that will dispatch a tracking call when the "page" components mount (componentDidMount()) -- the only scenario where this will trigger multiple times is if the page is navigated away from and then back to (in which case, we want the "duplicate" tracking call).

My only guess is that you're decorating a life-cycle method that you may not actually want (because it gets triggered multiple times, such as componentDidUpdate() or render()). If you can provide an example repo, or put something up on codesandbox, I'd be happy to take a look.

Tracking parent components

Ah, this is precisely the problem that react-tracking was built to solve, so you're in the right place 😄

The paradigm is:, "Decorate" your components with the tracking information that you have, when and where you have it.

So, for your scenario, as a simplified example, let's say your component hierarchy looks like this:

  <Page>
    <Recommendations>
      <Product />
      <Product />
    </Recommendations>
    <OtherUsersLiked>
      <Product />
      <Product />
    </OtherUsersLiked>
  </Page>

And you decorate with tracking like so:

@track({ page: 'page' })
class Page extends Component { ... }

@track({ slider: 'recommendations' })
class Recommendations extends Component { ... }
@track({ slider: 'other-users-liked' })
class OtherUsersLiked extends Component { ... }

@track({ product: 'product' })
class Product extends Component { ... }

Then, if a user were to click on a Product within the Recommendations slider, the following would get dispatched:

{
  page: 'page',
  slider: 'recommendations',
  product: 'product'
}

You have the context ("slider" and "page") of where the Product was clicked. This is because you've decorated your components with the tracking information you had, where you had it.


I hope that was helpful. If it was, I would love a documentation PR to make this more explicit and/or easier to follow in the docs. Maybe we need a "common patterns" or FAQ doc to go over common questions like this?

daniel-winter commented 6 years ago

Hi @tizmagik, thank you for your detailed answer.

Duplicate data

We're evaluating our code right now. This takes a lot of time, so I'm sorry for the delay of a proper response.

In the meantime I've another question regarding the usage of the decorator. We've currently a problem using arrow functions as event callbacks with the decorator. We're using arrow functions to prevent binding in the constructor. For example:

class Product extends Component {
    ... bla bla ...

    @track()
    handleClick = (event) => {
        console.log('Clicked!');
    }
    render() {
        return (
            <button onClick={this.handleClick}>Do it</button>
        );
    }
}
export default Product;

In this case handleClick does not gets executed at all. Do you have any idea on that? We have also checkout your codesandbox (https://codesandbox.io/s/pj43j286nj) and forked it here (https://codesandbox.io/s/pj43j286nj) but we haven’t found any working solution.

Tracking parent components

Thank you for your explanation. Indeed a nice feature! But we're facing the problem of using the same parent component more than once. Like this:

  <Page>
    <Slider>
      <Product uid="a" />
      <Product uid="b" />
    </Slider>
    <SomeOtherStuff />
    <Slider>
      <Product uid="a" />
      <Product uid="c" />
    </Slider>
  </Page>

So the context of the parent component is just 'imaginary', since both parents are the same component. But maybe we could distinguish them by adding otherwise unused props like <Slider trackAs="recommendations">

Nevertheless the parent decorators don't get executed on (for example) a Product „hover“ or Product „click“. So tracking which Product was clicked or hovered is possible, but at that time we're missing the information, if it was in Slider 1 or 2. Do you have any suggestions for that challange?

Thanks in advance!

tizmagik commented 6 years ago

Decorating class methods should work fine (as can be seen in the codesandbox example). Make sure you also decorate the class declaration itself, that's required for decorating within the class to work properly.

@track() // required
class Product extends Component {
  @track({ event: 'clicked' })
  handleClick = () => {}
}

Also, make sure your tracking objects have data in them (at least one property for the entire tree). By default, empty objects are not dispatched.


Yes, I would solve as you suggested, provide an otherwise unused prop so that the tracking context of "which" slider can be determined.

@track(props => ({
  slider: props.trackAs,
}))
class Slider extends Component {}

Then render them as so:

<Slider trackAs="recommendations" />
<Slider trackAs="otherusersliked" />
rickgaurav commented 6 years ago

Decorating class methods should work fine (as can be seen in the codesandbox example). Make sure you also decorate the class declaration itself, that's required for decorating within the class to work properly.

I think this should be more prominent in readme.md. Can raise a PR, if you agree.