Skip to content

Commit e20abc3

Browse files
authored
Add forwardRef (#153)
* fix #87, add forwardRef option * fix #87, fix some spell errors, set forwardRef to false as default
1 parent 599242e commit e20abc3

File tree

3 files changed

+57
-6
lines changed

3 files changed

+57
-6
lines changed

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ import track, { useTracking } from 'react-tracking';
3232
- `dispatch`, which is a function to use instead of the default dispatch behavior. See the section on custom `dispatch()` later in this document.
3333
- `dispatchOnMount`, when set to `true`, dispatches the tracking data when the component mounts to the DOM. When provided as a function will be called on componentDidMount with all of the tracking context data as the only argument.
3434
- `process`, which is a function that can be defined once on some top-level component, used for selectively dispatching tracking events based on each component's tracking data. See more details later in this document.
35+
- `forwardRef`, when set to `true`, adding a ref to the wrapped component will actually return the instance of the underlying component. Default is `false`.
3536

3637
#### `tracking` prop
3738

src/__tests__/e2e.test.js

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -866,4 +866,37 @@ describe('e2e', () => {
866866
status: 'failed',
867867
});
868868
});
869+
870+
it('can access wrapped component by ref', async () => {
871+
const focusFn = jest.fn();
872+
@track({}, { forwardRef: true })
873+
class Child extends React.Component {
874+
focus = focusFn;
875+
876+
render() {
877+
return 'child';
878+
}
879+
}
880+
881+
class Parent extends React.Component {
882+
componentDidMount() {
883+
this.child.focus();
884+
}
885+
886+
render() {
887+
return (
888+
<Child
889+
ref={el => {
890+
this.child = el;
891+
}}
892+
/>
893+
);
894+
}
895+
}
896+
897+
const parent = await mount(<Parent />);
898+
899+
expect(parent.instance().child).not.toBeNull();
900+
expect(focusFn).toHaveBeenCalledTimes(1);
901+
});
869902
});

src/withTrackingComponentDecorator.js

Lines changed: 23 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -15,13 +15,18 @@ export const ReactTrackingContext = React.createContext({});
1515

1616
export default function withTrackingComponentDecorator(
1717
trackingData = {},
18-
{ dispatch = dispatchTrackingEvent, dispatchOnMount = false, process } = {}
18+
{
19+
dispatch = dispatchTrackingEvent,
20+
dispatchOnMount = false,
21+
process,
22+
forwardRef = false,
23+
} = {}
1924
) {
2025
return DecoratedComponent => {
2126
const decoratedComponentName =
2227
DecoratedComponent.displayName || DecoratedComponent.name || 'Component';
2328

24-
function WithTracking(props) {
29+
function WithTracking({ rtFwdRef, ...props }) {
2530
const { tracking } = useContext(ReactTrackingContext);
2631
const latestProps = useRef(props);
2732

@@ -118,20 +123,32 @@ export default function withTrackingComponentDecorator(
118123
[getTrackingDispatcher, getTrackingDataFn, getProcessFn]
119124
);
120125

126+
const propsToBePassed = useMemo(
127+
() => (forwardRef ? { ...props, ref: rtFwdRef } : props),
128+
[props, rtFwdRef]
129+
);
130+
121131
return useMemo(
122132
() => (
123133
<ReactTrackingContext.Provider value={contextValue}>
124-
<DecoratedComponent {...props} tracking={trackingProp} />
134+
<DecoratedComponent {...propsToBePassed} tracking={trackingProp} />
125135
</ReactTrackingContext.Provider>
126136
),
127-
[contextValue, props, trackingProp]
137+
[contextValue, trackingProp, propsToBePassed]
128138
);
129139
}
130140

131-
WithTracking.displayName = `WithTracking(${decoratedComponentName})`;
141+
if (forwardRef) {
142+
const forwarded = React.forwardRef((props, ref) => (
143+
<WithTracking {...props} rtFwdRef={ref} />
144+
));
145+
forwarded.displayName = `WithTracking(${decoratedComponentName})`;
146+
hoistNonReactStatic(forwarded, DecoratedComponent);
147+
return forwarded;
148+
}
132149

150+
WithTracking.displayName = `WithTracking(${decoratedComponentName})`;
133151
hoistNonReactStatic(WithTracking, DecoratedComponent);
134-
135152
return WithTracking;
136153
};
137154
}

0 commit comments

Comments
 (0)