diff --git a/src/components/AddPlaidBankAccount.js b/src/components/AddPlaidBankAccount.js
index ff97c9be24a6..d50fad0bd2f0 100644
--- a/src/components/AddPlaidBankAccount.js
+++ b/src/components/AddPlaidBankAccount.js
@@ -1,5 +1,5 @@
import _ from 'underscore';
-import React from 'react';
+import React, {useEffect, useRef, useCallback} from 'react';
import {ActivityIndicator, View} from 'react-native';
import PropTypes from 'prop-types';
import {withOnyx} from 'react-native-onyx';
@@ -10,17 +10,16 @@ import * as BankAccounts from '../libs/actions/BankAccounts';
import ONYXKEYS from '../ONYXKEYS';
import styles from '../styles/styles';
import themeColors from '../styles/themes/default';
-import compose from '../libs/compose';
-import withLocalize, {withLocalizePropTypes} from './withLocalize';
import Picker from './Picker';
import {plaidDataPropTypes} from '../pages/ReimbursementAccount/plaidDataPropTypes';
import Text from './Text';
import getBankIcon from './Icon/BankIcons';
import Icon from './Icon';
import FullPageOfflineBlockingView from './BlockingViews/FullPageOfflineBlockingView';
-import {withNetwork} from './OnyxProvider';
import CONST from '../CONST';
import KeyboardShortcut from '../libs/KeyboardShortcut';
+import useLocalize from '../hooks/useLocalize';
+import useNetwork from '../hooks/useNetwork';
const propTypes = {
/** Contains plaid data */
@@ -52,8 +51,6 @@ const propTypes = {
/** Are we adding a withdrawal account? */
allowDebit: PropTypes.bool,
-
- ...withLocalizePropTypes,
};
const defaultProps = {
@@ -68,172 +65,163 @@ const defaultProps = {
bankAccountID: 0,
};
-class AddPlaidBankAccount extends React.Component {
- constructor(props) {
- super(props);
-
- this.getPlaidLinkToken = this.getPlaidLinkToken.bind(this);
- this.subscribedKeyboardShortcuts = [];
- }
-
- componentDidMount() {
- this.subscribeToNavigationShortcuts();
-
- // If we're coming from Plaid OAuth flow then we need to reuse the existing plaidLinkToken
- if (this.isAuthenticatedWithPlaid()) {
- return;
- }
-
- BankAccounts.openPlaidBankLogin(this.props.allowDebit, this.props.bankAccountID);
- }
-
- componentDidUpdate(prevProps) {
- if (!prevProps.network.isOffline || this.props.network.isOffline || this.isAuthenticatedWithPlaid()) {
- return;
- }
-
- // If we are coming back from offline and we haven't authenticated with Plaid yet, we need to re-run our call to kick off Plaid
- BankAccounts.openPlaidBankLogin(this.props.allowDebit, this.props.bankAccountID);
- }
+function AddPlaidBankAccount({plaidData, selectedPlaidAccountID, plaidLinkToken, onExitPlaid, onSelect, text, receivedRedirectURI, plaidLinkOAuthToken, bankAccountID, allowDebit}) {
+ const subscribedKeyboardShortcuts = useRef([]);
+ const previousNetworkState = useRef();
- componentWillUnmount() {
- this.unsubscribeToNavigationShortcuts();
- }
+ const {translate} = useLocalize();
+ const {isOffline} = useNetwork();
/**
* @returns {String}
*/
- getPlaidLinkToken() {
- if (this.props.plaidLinkToken) {
- return this.props.plaidLinkToken;
+ const getPlaidLinkToken = () => {
+ if (plaidLinkToken) {
+ return plaidLinkToken;
}
- if (this.props.receivedRedirectURI && this.props.plaidLinkOAuthToken) {
- return this.props.plaidLinkOAuthToken;
+ if (receivedRedirectURI && plaidLinkOAuthToken) {
+ return plaidLinkOAuthToken;
}
- }
+ };
/**
* @returns {Boolean}
+ * I'm using useCallback so the useEffect which uses this function doesn't run on every render.
*/
- isAuthenticatedWithPlaid() {
- return (
- (this.props.receivedRedirectURI && this.props.plaidLinkOAuthToken) ||
- !_.isEmpty(lodashGet(this.props.plaidData, 'bankAccounts')) ||
- !_.isEmpty(lodashGet(this.props.plaidData, 'errors'))
- );
- }
+ const isAuthenticatedWithPlaid = useCallback(
+ () => (receivedRedirectURI && plaidLinkOAuthToken) || !_.isEmpty(lodashGet(plaidData, 'bankAccounts')) || !_.isEmpty(lodashGet(plaidData, 'errors')),
+ [plaidData, plaidLinkOAuthToken, receivedRedirectURI],
+ );
/**
* Blocks the keyboard shortcuts that can navigate
*/
- subscribeToNavigationShortcuts() {
+ const subscribeToNavigationShortcuts = () => {
// find and block the shortcuts
const shortcutsToBlock = _.filter(CONST.KEYBOARD_SHORTCUTS, (x) => x.type === CONST.KEYBOARD_SHORTCUTS_TYPES.NAVIGATION_SHORTCUT);
- this.subscribedKeyboardShortcuts = _.map(shortcutsToBlock, (shortcut) =>
+ subscribedKeyboardShortcuts.current = _.map(shortcutsToBlock, (shortcut) =>
KeyboardShortcut.subscribe(
shortcut.shortcutKey,
() => {}, // do nothing
shortcut.descriptionKey,
shortcut.modifiers,
false,
- () => lodashGet(this.props.plaidData, 'bankAccounts', []).length > 0, // start bubbling when there are bank accounts
+ () => lodashGet(plaidData, 'bankAccounts', []).length > 0, // start bubbling when there are bank accounts
),
);
- }
+ };
/**
* Unblocks the keyboard shortcuts that can navigate
*/
- unsubscribeToNavigationShortcuts() {
- _.each(this.subscribedKeyboardShortcuts, (unsubscribe) => unsubscribe());
- this.subscribedKeyboardShortcuts = [];
- }
+ const unsubscribeToNavigationShortcuts = () => {
+ _.each(subscribedKeyboardShortcuts.current, (unsubscribe) => unsubscribe());
+ subscribedKeyboardShortcuts.current = [];
+ };
- render() {
- const plaidBankAccounts = lodashGet(this.props.plaidData, 'bankAccounts') || [];
- const token = this.getPlaidLinkToken();
- const options = _.map(plaidBankAccounts, (account) => ({
- value: account.plaidAccountID,
- label: `${account.addressName} ${account.mask}`,
- }));
- const {icon, iconSize} = getBankIcon();
- const plaidErrors = lodashGet(this.props.plaidData, 'errors');
- const plaidDataErrorMessage = !_.isEmpty(plaidErrors) ? _.chain(plaidErrors).values().first().value() : '';
- const bankName = lodashGet(this.props.plaidData, 'bankName');
-
- // Plaid Link view
- if (!plaidBankAccounts.length) {
- return (
-
- {lodashGet(this.props.plaidData, 'isLoading') && (
-
-
-
- )}
- {Boolean(plaidDataErrorMessage) && {plaidDataErrorMessage}}
- {Boolean(token) && !bankName && (
- {
- Log.info('[PlaidLink] Success!');
- BankAccounts.openPlaidBankAccountSelector(publicToken, metadata.institution.name, this.props.allowDebit, this.props.bankAccountID);
- }}
- onError={(error) => {
- Log.hmmm('[PlaidLink] Error: ', error.message);
- }}
- // User prematurely exited the Plaid flow
- // eslint-disable-next-line react/jsx-props-no-multi-spaces
- onExit={this.props.onExitPlaid}
- receivedRedirectURI={this.props.receivedRedirectURI}
- />
- )}
-
- );
+ useEffect(() => {
+ subscribeToNavigationShortcuts();
+
+ // If we're coming from Plaid OAuth flow then we need to reuse the existing plaidLinkToken
+ if (isAuthenticatedWithPlaid()) {
+ return unsubscribeToNavigationShortcuts;
}
+ BankAccounts.openPlaidBankLogin(allowDebit, bankAccountID);
+ return unsubscribeToNavigationShortcuts;
- // Plaid bank accounts view
+ // disabling this rule, as we want this to run only on the first render
+ // eslint-disable-next-line react-hooks/exhaustive-deps
+ }, []);
+
+ useEffect(() => {
+ // If we are coming back from offline and we haven't authenticated with Plaid yet, we need to re-run our call to kick off Plaid
+ // previousNetworkState.current also makes sure that this doesn't run on the first render.
+ if (previousNetworkState.current && !isOffline && !isAuthenticatedWithPlaid()) {
+ BankAccounts.openPlaidBankLogin(allowDebit, bankAccountID);
+ }
+ previousNetworkState.current = isOffline;
+ }, [allowDebit, bankAccountID, isAuthenticatedWithPlaid, isOffline]);
+
+ const plaidBankAccounts = lodashGet(plaidData, 'bankAccounts') || [];
+ const token = getPlaidLinkToken();
+ const options = _.map(plaidBankAccounts, (account) => ({
+ value: account.plaidAccountID,
+ label: `${account.addressName} ${account.mask}`,
+ }));
+ const {icon, iconSize} = getBankIcon();
+ const plaidErrors = lodashGet(plaidData, 'errors');
+ const plaidDataErrorMessage = !_.isEmpty(plaidErrors) ? _.chain(plaidErrors).values().first().value() : '';
+ const bankName = lodashGet(plaidData, 'bankName');
+
+ // Plaid Link view
+ if (!plaidBankAccounts.length) {
return (
- {!_.isEmpty(this.props.text) && {this.props.text}}
-
-
- {bankName}
-
-
-
+
+
+ )}
+ {Boolean(plaidDataErrorMessage) && {plaidDataErrorMessage}}
+ {Boolean(token) && !bankName && (
+ {
+ Log.info('[PlaidLink] Success!');
+ BankAccounts.openPlaidBankAccountSelector(publicToken, metadata.institution.name, allowDebit, bankAccountID);
+ }}
+ onError={(error) => {
+ Log.hmmm('[PlaidLink] Error: ', error.message);
}}
- value={this.props.selectedPlaidAccountID}
+ // User prematurely exited the Plaid flow
+ // eslint-disable-next-line react/jsx-props-no-multi-spaces
+ onExit={onExitPlaid}
+ receivedRedirectURI={receivedRedirectURI}
/>
-
+ )}
);
}
+
+ // Plaid bank accounts view
+ return (
+
+ {!_.isEmpty(text) && {text}}
+
+
+ {bankName}
+
+
+
+
+
+ );
}
AddPlaidBankAccount.propTypes = propTypes;
AddPlaidBankAccount.defaultProps = defaultProps;
-
-export default compose(
- withLocalize,
- withNetwork(),
- withOnyx({
- plaidLinkToken: {
- key: ONYXKEYS.PLAID_LINK_TOKEN,
- initWithStoredValues: false,
- },
- }),
-)(AddPlaidBankAccount);
+AddPlaidBankAccount.displayName = 'AddPlaidBankAccount';
+
+export default withOnyx({
+ plaidLinkToken: {
+ key: ONYXKEYS.PLAID_LINK_TOKEN,
+ initWithStoredValues: false,
+ },
+})(AddPlaidBankAccount);