-
Notifications
You must be signed in to change notification settings - Fork 123
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Expand React Hooks API support #138
Comments
The tricky thing here is that the It would look something like this: track decorator/HoC function MyComponent({ data, tracking }) {
const handleClick = () => {
tracking.trackEvent({
element: 'My button',
label: 'Click me',
});
};
return (
<div>
<button type="button" onClick={handleClick}>Click me</button>
Some other JSX here
</div>
);
}
export default track(props => ({
module: {
name: 'My module',
},
someData: props.data,
}))(MyComponent); useTracking hook function MyComponent({ data }) {
const [TrackingProvider, tracking] = useTracking({
module: {
name: 'My module',
},
someData: data,
});
const handleClick = () => {
tracking.trackEvent({
element: 'My button',
label: 'Click me',
});
};
return (
<TrackingProvider>
<button type="button" onClick={handleClick}>Click me</button>
Some other JSX here
</TrackingProvider>
);
}
export default MyComponent; I'm looking for feedback re: the ergonomics of this API, particularly whether people would find it to be a dealbreaker for the onus to be on the consumer of the library to wrap their component's JSX in the provider returned by the hook. |
Thanks for the exploration, @bgergen ! I'm not crazy about having to add I'm not sure if that's practically possible, but theoretically I feel like it should be. I'll need to experiment with it a bit, but curious if you think that makes sense? I'm also wondering if there's any prior-art of something similar in the wild? |
@tizmagik Yeah, I had the exact same thought when I was trying to work through how to avoid wrapping the markup in a new provider. I considered the possibility of having one provider near the root of the component tree, which would make an updater function available to all consumers. Where my mental model broke down was in trying to envision how a I definitely agree both that this feels like it should be possible, and that if we can make it work on a practical level, it would result in a far preferable API to the one I proposed above. I will continue to do further research on this and would also be down to talk it through in further depth. |
Somehow you need to be aware of what branch of the tree you're in. One possible solution is to use the DOM tree as the source of truth. You could use event bubbling to pass tracking data up the DOM tree. To add contexts you could add listeners for the bubbling events that would append their contexts to the event as it bubbles up the DOM. Eventually once the event hits the root (or Provider) it would grab all the appended contexts off the event and add it to the event queue. I hacked together a codesandbox using CustomEvents to bubble tracking data up to the root provider and tack on tracking contexts along the way. Edit: |
Ah! Both brilliant suggestions, @ianduvall ! Thank you. I think the VDOM/ErrorBoundary approach might actually work out better because for any solution it needs to:
I'll munch on this some more when I get a chance, but if you have a POC codesandbox for that ErrorBoundary approach I'd love to take a look. Thanks again! |
So I thought about the
Another point that doesn't make me feel great about this approach is it breaks React's unidirectional data flow pattern. Making an exception for this doesn't seem worth it. |
Ah, good point. Thanks for thinking through that approach! Maybe the event bubbling approach might work then, but will have to think through how to handle it for react-native |
I just learned "[Context] Providers can be nested to override values deeper within the tree" from the docs. Similar to @bgergen 's approach of using Context Providers, you could use Providers to create new tracking contexts by merging its ancestor's tracking context with a new context and passing that to the Provider to override the context value in that subtree. I forked my original codesandbox and updated it with this provider value override approach. You provide the root tracking context to @tizmagik I know you mentioned hoping to avoid adding Providers to component tree but I can't think of a better way to differentiate what branch of the VDOM you're in. |
Yea, I think that's the problem, because otherwise it's not much different than just using the HoC API. I wonder if it would even be worth introducing this nested context provider API vs just recommending folks stick to the HoC API. I'm not sure the added complexity to the API surface is worth it. 🤔 |
Feels like we could really use something like this: facebook/react#4595 |
I think I'm warming up to the idea of returning a component from the It feels like something around these lines should be possible: test('will deep-merge tracking data', () => {
const mockDispatchTrackingEvent = jest.fn();
const testData1 = { key: { x: 1, y: 1 } };
const testData2 = { key: { x: 2, z: 2 }, page: 'TestDeepMerge' };
const TestData1 = ({ children }) => {
const { Track } = useTracking();
return (
<Track data={testData1} dispatch={mockDispatchTrackingEvent}>
{children}
</Track>
);
};
const TestData3 = () => {
const { Track } = useTracking();
return (
<Track data={{ key: { x: 3, y: 3 } }} dispatchOnMount>
<div />
</Track>
);
};
const TestData2 = () => {
const { Track } = useTracking();
return (
<Track data={testData2}>
<TestData3 />
</Track>
);
};
mount(
<TestData1>
<TestData2 />
</TestData1>
);
expect(mockDispatchTrackingEvent).toHaveBeenCalledWith({
page: 'TestDeepMerge',
key: { x: 3, y: 3, z: 2 },
});
}); Thoughts? |
I think I like this. I ran into one of the nuisances of not having something like this available recently, which is the need to swap out the hook for the HoC whenever you want a component to add contextual data where it didn't do so before. I want to make sure I understand what you mean by:
Does this just mean that |
Yes, in fact, I think this is the only way to enable adding contextual tracking to the tree via a Hooks-only API. I started some work on this in PR #165 which basically takes all of our e2e tests and re-writes them to only use the After rewriting a few of the tests I actually ended up settling on what you originally suggested here @bgergen but instead of calling it Curious to get everyone's feedback on how that API feels? If it looks good, and anyone's interested, feel free to code against those tests for a working implementation. |
Closing this issue with the release of v8.1.0 |
To build on the work done in #124 (issue #120) -- we currently support "grabbing" the contextual tracking data via
useTracking()
hook, but we don't currently provide a way to add additional tracking context via hooks. I'm not entirely sure if doing so would be an anti-pattern, but if it's possible and doesn't break React Hooks paradigm/patterns to do so, it would be great to not have to use thetrack()
HoC wrapper and just use a React Hooks API.The text was updated successfully, but these errors were encountered: