-
Notifications
You must be signed in to change notification settings - Fork 124
/
useTrackingImpl.js
121 lines (107 loc) · 3.49 KB
/
useTrackingImpl.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
import { useCallback, useContext, useEffect, useMemo, useRef } from 'react';
import merge from 'deepmerge';
import ReactTrackingContext from './ReactTrackingContext';
import dispatchTrackingEvent from './dispatchTrackingEvent';
export default function useTrackingImpl(trackingData, options) {
const { tracking } = useContext(ReactTrackingContext);
const latestData = useRef(trackingData);
const latestOptions = useRef(options);
useEffect(() => {
// store the latest data & options in a mutable ref to prevent
// dependencies from changing when the consumer passes in non-memoized objects
// same approach that we use for props in withTrackingComponentDecorator
latestData.current = trackingData;
latestOptions.current = options;
});
const {
dispatch = dispatchTrackingEvent,
dispatchOnMount = false,
process,
} = useMemo(() => latestOptions.current || {}, []);
const getProcessFn = useCallback(
() => tracking && tracking.process,
[tracking]
);
const getOwnTrackingData = useCallback(() => {
const data = latestData.current;
const ownTrackingData = typeof data === 'function' ? data() : data;
return ownTrackingData || {};
}, []);
const getTrackingDataFn = useCallback(() => {
const contextGetTrackingData =
(tracking && tracking.getTrackingData) || getOwnTrackingData;
return () =>
contextGetTrackingData === getOwnTrackingData
? getOwnTrackingData()
: merge(
contextGetTrackingData(),
getOwnTrackingData(),
(latestOptions.current || {}).mergeOptions
);
}, [getOwnTrackingData, tracking]);
const getTrackingDispatcher = useCallback(() => {
const contextDispatch = (tracking && tracking.dispatch) || dispatch;
return data =>
contextDispatch(
merge(
getOwnTrackingData(),
data || {},
(latestOptions.current || {}).mergeOptions
)
);
}, [getOwnTrackingData, tracking, dispatch]);
const trackEvent = useCallback(
(data = {}) => {
getTrackingDispatcher()(data);
},
[getTrackingDispatcher]
);
useEffect(() => {
const contextProcess = getProcessFn();
const getTrackingData = getTrackingDataFn();
if (contextProcess && process) {
// eslint-disable-next-line
console.error(
'[react-tracking] options.process should be defined once on a top-level component'
);
}
if (
typeof contextProcess === 'function' &&
typeof dispatchOnMount === 'function'
) {
trackEvent(
merge(
contextProcess(getOwnTrackingData()) || {},
dispatchOnMount(getTrackingData()) || {},
(latestOptions.current || {}).mergeOptions
)
);
} else if (typeof contextProcess === 'function') {
const processed = contextProcess(getOwnTrackingData());
if (processed || dispatchOnMount === true) {
trackEvent(processed);
}
} else if (typeof dispatchOnMount === 'function') {
trackEvent(dispatchOnMount(getTrackingData()));
} else if (dispatchOnMount === true) {
trackEvent();
}
}, [
getOwnTrackingData,
getProcessFn,
getTrackingDataFn,
trackEvent,
dispatchOnMount,
process,
]);
return useMemo(
() => ({
tracking: {
dispatch: getTrackingDispatcher(),
getTrackingData: getTrackingDataFn(),
process: getProcessFn() || process,
},
}),
[getTrackingDispatcher, getTrackingDataFn, getProcessFn, process]
);
}