diff --git a/src/Expensify.js b/src/Expensify.js index 86b06b6cfd700..27800bb258b94 100644 --- a/src/Expensify.js +++ b/src/Expensify.js @@ -9,7 +9,7 @@ import Ion from './lib/Ion'; import * as ActiveClientManager from './lib/ActiveClientManager'; import {verifyAuthToken} from './lib/actions/Session'; import IONKEYS from './IONKEYS'; -import WithIon from './components/WithIon'; +import withIon from './components/withIon'; import styles from './style/StyleSheet'; import { @@ -99,7 +99,7 @@ class Expensify extends Component { Expensify.propTypes = propTypes; Expensify.defaultProps = defaultProps; -export default WithIon({ +export default withIon({ redirectTo: { key: IONKEYS.APP_REDIRECT_TO, loader: () => { diff --git a/src/components/WithIon.js b/src/components/withIon.js similarity index 98% rename from src/components/WithIon.js rename to src/components/withIon.js index a86408dd9ee27..e43d803718627 100644 --- a/src/components/WithIon.js +++ b/src/components/withIon.js @@ -21,7 +21,7 @@ function getDisplayName(component) { export default function (mapIonToState) { return (WrappedComponent) => { - class WithIon extends React.Component { + class withIon extends React.Component { constructor(props) { super(props); @@ -145,7 +145,7 @@ export default function (mapIonToState) { } } - WithIon.displayName = `WithIon(${getDisplayName(WrappedComponent)})`; - return WithIon; + withIon.displayName = `WithIon(${getDisplayName(WrappedComponent)})`; + return withIon; }; } diff --git a/src/lib/compose.js b/src/lib/compose.js new file mode 100644 index 0000000000000..f97cf8bb2d6e7 --- /dev/null +++ b/src/lib/compose.js @@ -0,0 +1,29 @@ +/** + * This is a utility function taken directly from Redux. (We don't want to add Redux as a dependency) + * It enables functional composition, useful for the chaining/composition of HOCs. + * + * For example, instead of: + * + * export default hoc1(config1, hoc2(config2, hoc3(config3)))(Component); + * + * Use this instead: + * + * export default compose( + * hoc1(config1), + * hoc2(config2), + * hoc3(config3), + * )(Component) + * + * @returns {*|(function(...[*]): *)|(function(*): *)} + */ +export default function compose(...funcs) { + if (funcs.length === 0) { + return arg => arg; + } + + if (funcs.length === 1) { + return funcs[0]; + } + + return funcs.reduce((a, b) => (...args) => a(b(...args))); +} diff --git a/src/page/HomePage/HeaderView.js b/src/page/HomePage/HeaderView.js index c18e8c3a4593c..3a74ddf5a6642 100644 --- a/src/page/HomePage/HeaderView.js +++ b/src/page/HomePage/HeaderView.js @@ -4,9 +4,10 @@ import PropTypes from 'prop-types'; import Text from '../../components/Text'; import styles from '../../style/StyleSheet'; import IONKEYS from '../../IONKEYS'; -import WithIon from '../../components/WithIon'; +import withIon from '../../components/withIon'; import {withRouter} from '../../lib/Router'; import LHNToggle from '../../../assets/images/icon-menu-toggle.png'; +import compose from '../../lib/compose'; const propTypes = { // Toggles the hamburger menu open and closed @@ -53,13 +54,17 @@ HeaderView.propTypes = propTypes; HeaderView.displayName = 'HeaderView'; HeaderView.defaultProps = defaultProps; -export default withRouter(WithIon({ - // Map this.props.reportName to the data for a specific report in the store, and bind it to the reportName property - // It uses the data returned from the props path (ie. the reportID) to replace %DATAFROMPROPS% in the key it - // binds to - reportName: { - key: `${IONKEYS.REPORT}_%DATAFROMPROPS%`, - path: 'reportName', - pathForProps: 'match.params.reportID', - }, -})(HeaderView)); +export default compose( + withRouter, + withIon({ + // Map this.props.reportName to the data for a specific report in the store, + // and bind it to the reportName property. + // It uses the data returned from the props path (ie. the reportID) to replace %DATAFROMPROPS% in the key it + // binds to + reportName: { + key: `${IONKEYS.REPORT}_%DATAFROMPROPS%`, + path: 'reportName', + pathForProps: 'match.params.reportID', + }, + }), +)(HeaderView); diff --git a/src/page/HomePage/MainView.js b/src/page/HomePage/MainView.js index bc47603b5d0b3..3e6610914eb92 100644 --- a/src/page/HomePage/MainView.js +++ b/src/page/HomePage/MainView.js @@ -3,10 +3,11 @@ import {View} from 'react-native'; import PropTypes from 'prop-types'; import _ from 'underscore'; import ReportView from './Report/ReportView'; -import WithIon from '../../components/WithIon'; +import withIon from '../../components/withIon'; import IONKEYS from '../../IONKEYS'; import styles from '../../style/StyleSheet'; import {withRouter} from '../../lib/Router'; +import compose from '../../lib/compose'; const propTypes = { // This comes from withRouter @@ -62,10 +63,13 @@ class MainView extends React.Component { MainView.propTypes = propTypes; MainView.defaultProps = defaultProps; -export default withRouter(WithIon({ - reports: { - key: `${IONKEYS.REPORT}_[0-9]+$`, - addAsCollection: true, - collectionID: 'reportID', - }, -})(MainView)); +export default compose( + withRouter, + withIon({ + reports: { + key: `${IONKEYS.REPORT}_[0-9]+$`, + addAsCollection: true, + collectionID: 'reportID', + }, + }), +)(MainView); diff --git a/src/page/HomePage/Report/ReportHistoryView.js b/src/page/HomePage/Report/ReportHistoryView.js index bc958f7240e47..416d7a49f0219 100644 --- a/src/page/HomePage/Report/ReportHistoryView.js +++ b/src/page/HomePage/Report/ReportHistoryView.js @@ -5,13 +5,14 @@ import _ from 'underscore'; import lodashGet from 'lodash.get'; import Text from '../../../components/Text'; import Ion from '../../../lib/Ion'; +import withIon from '../../../components/withIon'; import {fetchHistory, updateLastReadActionID} from '../../../lib/actions/Report'; -import WithIon from '../../../components/WithIon'; import IONKEYS from '../../../IONKEYS'; import ReportHistoryItem from './ReportHistoryItem'; import styles from '../../../style/StyleSheet'; import {withRouter} from '../../../lib/Router'; import ReportHistoryPropsTypes from './ReportHistoryPropsTypes'; +import compose from '../../../lib/compose'; const propTypes = { // The ID of the report actions will be created for @@ -163,16 +164,19 @@ ReportHistoryView.propTypes = propTypes; ReportHistoryView.defaultProps = defaultProps; const key = `${IONKEYS.REPORT_HISTORY}_%DATAFROMPROPS%`; -export default withRouter(WithIon({ - authToken: { - key: IONKEYS.SESSION, - path: 'authToken', - prefillWithKey: IONKEYS.SESSION, - }, - reportHistory: { - key, - loader: fetchHistory, - loaderParams: ['%DATAFROMPROPS%'], - pathForProps: 'reportID', - }, -})(ReportHistoryView)); +export default compose( + withRouter, + withIon({ + authToken: { + key: IONKEYS.SESSION, + path: 'authToken', + prefillWithKey: IONKEYS.SESSION, + }, + reportHistory: { + key, + loader: fetchHistory, + loaderParams: ['%DATAFROMPROPS%'], + pathForProps: 'reportID', + }, + }), +)(ReportHistoryView); diff --git a/src/page/HomePage/SidebarLink.js b/src/page/HomePage/SidebarLink.js index 7af619445c313..322a38e793516 100644 --- a/src/page/HomePage/SidebarLink.js +++ b/src/page/HomePage/SidebarLink.js @@ -5,8 +5,9 @@ import Text from '../../components/Text'; import {withRouter} from '../../lib/Router'; import IONKEYS from '../../IONKEYS'; import styles from '../../style/StyleSheet'; -import WithIon from '../../components/WithIon'; +import withIon from '../../components/withIon'; import PressableLink from '../../components/PressableLink'; +import compose from '../../lib/compose'; const propTypes = { // The ID of the report for this link @@ -57,11 +58,14 @@ SidebarLink.displayName = 'SidebarLink'; SidebarLink.propTypes = propTypes; SidebarLink.defaultProps = defaultProps; -export default withRouter(WithIon({ - isUnread: { - key: `${IONKEYS.REPORT}_%DATAFROMPROPS%`, - path: 'hasUnread', - defaultValue: false, - pathForProps: 'reportID', - } -})(SidebarLink)); +export default compose( + withRouter, + withIon({ + isUnread: { + key: `${IONKEYS.REPORT}_%DATAFROMPROPS%`, + path: 'hasUnread', + defaultValue: false, + pathForProps: 'reportID', + } + }), +)(SidebarLink); diff --git a/src/page/HomePage/SidebarView.js b/src/page/HomePage/SidebarView.js index f7edc1510594b..a858f704a901c 100644 --- a/src/page/HomePage/SidebarView.js +++ b/src/page/HomePage/SidebarView.js @@ -10,7 +10,7 @@ import Text from '../../components/Text'; import {signOut} from '../../lib/actions/Session'; import {fetch as getPersonalDetails} from '../../lib/actions/PersonalDetails'; import styles, {getSafeAreaMargins} from '../../style/StyleSheet'; -import WithIon from '../../components/WithIon'; +import withIon from '../../components/withIon'; import IONKEYS from '../../IONKEYS'; import {fetchAll} from '../../lib/actions/Report'; import SidebarLink from './SidebarLink'; @@ -149,7 +149,7 @@ class SidebarView extends React.Component { SidebarView.propTypes = propTypes; SidebarView.defaultProps = defaultProps; -export default WithIon({ +export default withIon({ // Map this.props.userDisplayName to the personal details key in the store and bind it to the displayName property // and load it with data from getPersonalDetails() userDisplayName: { diff --git a/src/page/SignInPage.js b/src/page/SignInPage.js index 723c85e6d5497..7d3f03cbee23e 100644 --- a/src/page/SignInPage.js +++ b/src/page/SignInPage.js @@ -9,10 +9,11 @@ import { View, } from 'react-native'; import PropTypes from 'prop-types'; +import compose from '../lib/compose'; import {withRouter} from '../lib/Router'; import {signIn} from '../lib/actions/Session'; import IONKEYS from '../IONKEYS'; -import WithIon from '../components/WithIon'; +import withIon from '../components/withIon'; import styles from '../style/StyleSheet'; import logo from '../../assets/images/expensify-logo_reversed.png'; @@ -128,7 +129,10 @@ class App extends Component { App.propTypes = propTypes; App.defaultProps = defaultProps; -export default withRouter(WithIon({ - // Bind this.props.error to the error in the session object - error: {key: IONKEYS.SESSION, path: 'error', defaultValue: null}, -})(App)); +export default compose( + withRouter, + withIon({ + // Bind this.props.error to the error in the session object + error: {key: IONKEYS.SESSION, path: 'error', defaultValue: null}, + }) +)(App);