Skip to content

Commit

Permalink
Add eslint react hooks plugin (#143)
Browse files Browse the repository at this point in the history
* use eslint react hook plugin

* Don't lie to react about dependencies (#144)

* Use mutable ref to store latest props

* Add comment
  • Loading branch information
tizmagik committed Oct 22, 2019
1 parent 748254d commit ee390cd
Show file tree
Hide file tree
Showing 4 changed files with 51 additions and 26 deletions.
4 changes: 3 additions & 1 deletion .eslintrc.js
@@ -1,13 +1,15 @@
module.exports = {
parser: 'babel-eslint',
extends: ['airbnb', 'plugin:prettier/recommended', 'prettier/react'],
plugins: ['prettier'],
plugins: ['prettier', 'react-hooks'],
env: {
jest: true,
browser: true,
},
rules: {
'prettier/prettier': 'error',
'react-hooks/rules-of-hooks': 'error',
'react-hooks/exhaustive-deps': 'warn',
'react/jsx-filename-extension': [
1,
{
Expand Down
32 changes: 19 additions & 13 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

7 changes: 4 additions & 3 deletions package.json
Expand Up @@ -70,7 +70,7 @@
]
},
"dependencies": {
"core-js": "^3.2.1",
"core-js": "^3.3.2",
"deepmerge": "^4.1.1",
"hoist-non-react-statics": "^3.3.0"
},
Expand All @@ -86,15 +86,16 @@
"babel-eslint": "^10.0.3",
"babel-plugin-transform-decorators-legacy": "^1.3.5",
"enzyme": "^3.10.0",
"enzyme-adapter-react-16": "^1.15.0",
"enzyme-adapter-react-16": "^1.15.1",
"eslint": "^6.5.1",
"eslint-config-airbnb": "^18.0.1",
"eslint-config-prettier": "^6.4.0",
"eslint-plugin-import": "^2.18.2",
"eslint-plugin-jsx-a11y": "^6.2.3",
"eslint-plugin-prettier": "^3.1.1",
"eslint-plugin-react": "^7.16.0",
"husky": "^3.0.8",
"eslint-plugin-react-hooks": "^2.1.2",
"husky": "^3.0.9",
"jest": "^24.9.0",
"lint-staged": "^9.4.2",
"prettier": "^1.18.2",
Expand Down
34 changes: 25 additions & 9 deletions src/withTrackingComponentDecorator.js
@@ -1,5 +1,11 @@
/* eslint-disable react/jsx-props-no-spreading */
import React, { useCallback, useContext, useEffect, useMemo } from 'react';
import React, {
useCallback,
useContext,
useEffect,
useMemo,
useRef,
} from 'react';
import PropTypes from 'prop-types';
import merge from 'deepmerge';
import hoistNonReactStatic from 'hoist-non-react-statics';
Expand All @@ -24,16 +30,26 @@ export default function withTrackingComponentDecorator(

function WithTracking(props) {
const { tracking } = useContext(ReactTrackingContext);
const latestProps = useRef(props);

const getProcessFn = useCallback(() => tracking && tracking.process, []);
useEffect(() => {
// keep the latest props in a mutable ref object to avoid creating
// additional dependency that could cause unnecessary re-renders
// see https://reactjs.org/docs/hooks-faq.html#what-can-i-do-if-my-effect-dependencies-change-too-often
latestProps.current = props;
});

// statically extract tracking.process for hook dependency
const trkProcess = tracking && tracking.process;
const getProcessFn = useCallback(() => trkProcess, [trkProcess]);

const getOwnTrackingData = useCallback(() => {
const ownTrackingData =
typeof trackingData === 'function'
? trackingData(props)
? trackingData(latestProps.current)
: trackingData;
return ownTrackingData || {};
}, [trackingData, props]);
}, []);

const getTrackingDataFn = useCallback(() => {
const contextGetTrackingData =
Expand All @@ -43,12 +59,12 @@ export default function withTrackingComponentDecorator(
contextGetTrackingData === getOwnTrackingData
? getOwnTrackingData()
: merge(contextGetTrackingData(), getOwnTrackingData());
}, [getOwnTrackingData]);
}, [getOwnTrackingData, tracking]);

const getTrackingDispatcher = useCallback(() => {
const contextDispatch = (tracking && tracking.dispatch) || dispatch;
return data => contextDispatch(merge(getOwnTrackingData(), data || {}));
}, [dispatch, getOwnTrackingData]);
}, [getOwnTrackingData, tracking]);

const trackEvent = useCallback(
(data = {}) => {
Expand Down Expand Up @@ -88,7 +104,7 @@ export default function withTrackingComponentDecorator(
} else if (dispatchOnMount === true) {
trackEvent();
}
}, []);
}, [getOwnTrackingData, getProcessFn, getTrackingDataFn, trackEvent]);

const trackingProp = useMemo(
() => ({
Expand All @@ -106,7 +122,7 @@ export default function withTrackingComponentDecorator(
process: getProcessFn() || process,
},
}),
[getTrackingDispatcher, getTrackingDataFn, getProcessFn, process]
[getTrackingDispatcher, getTrackingDataFn, getProcessFn]
);

return useMemo(
Expand All @@ -115,7 +131,7 @@ export default function withTrackingComponentDecorator(
<DecoratedComponent {...props} tracking={trackingProp} />
</ReactTrackingContext.Provider>
),
[contextValue, trackingProp]
[contextValue, props, trackingProp]
);
}

Expand Down

0 comments on commit ee390cd

Please sign in to comment.