diff --git a/src/components/MoneyRequestReportView/MoneyRequestReportTransactionList.tsx b/src/components/MoneyRequestReportView/MoneyRequestReportTransactionList.tsx index f1ad115c88786..57843fea723cb 100644 --- a/src/components/MoneyRequestReportView/MoneyRequestReportTransactionList.tsx +++ b/src/components/MoneyRequestReportView/MoneyRequestReportTransactionList.tsx @@ -307,8 +307,7 @@ function MoneyRequestReportTransactionList({ return groupTransactionsByTag(sortedTransactions, report, localeCompare); } return groupTransactionsByCategory(sortedTransactions, report, localeCompare); - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [sortedTransactions, currentGroupBy, report?.reportID, localeCompare, shouldShowGroupedTransactions]); + }, [sortedTransactions, currentGroupBy, report, localeCompare, shouldShowGroupedTransactions]); const visualOrderTransactionIDs = useMemo(() => { if (!shouldShowGroupedTransactions || groupedTransactions.length === 0) { diff --git a/src/components/TabSelector/TabSelectorBase.tsx b/src/components/TabSelector/TabSelectorBase.tsx index be7a412814c7c..a3c7670fc485a 100644 --- a/src/components/TabSelector/TabSelectorBase.tsx +++ b/src/components/TabSelector/TabSelectorBase.tsx @@ -1,4 +1,4 @@ -import React, {useEffect, useLayoutEffect, useRef, useState} from 'react'; +import React, {useCallback, useEffect, useLayoutEffect, useMemo, useRef, useState} from 'react'; import {View} from 'react-native'; // eslint-disable-next-line no-restricted-imports import type {Animated} from 'react-native'; @@ -74,7 +74,7 @@ function TabSelectorBase({ const routesLength = tabs.length; - const defaultAffectedAnimatedTabs = Array.from({length: routesLength}, (_v, i) => i); + const defaultAffectedAnimatedTabs = useMemo(() => Array.from({length: routesLength}, (_v, i) => i), [routesLength]); const [affectedAnimatedTabs, setAffectedAnimatedTabs] = useState(defaultAffectedAnimatedTabs); const viewRef = useRef(null); const [selectorWidth, setSelectorWidth] = useState(0); @@ -92,12 +92,12 @@ function TabSelectorBase({ return () => clearTimeout(timerID); }, [defaultAffectedAnimatedTabs, activeIndex]); - const measure = () => { + const measure = useCallback(() => { viewRef.current?.measureInWindow((x, _y, width) => { setSelectorX(x); setSelectorWidth(width); }); - }; + }, []); // Measure location/width after initial mount and when layout animations settle. useLayoutEffect(() => { diff --git a/src/hooks/useTransactionViolationOfWorkspace.tsx b/src/hooks/useTransactionViolationOfWorkspace.tsx index 7ee96534c5f01..74d8d8c45c498 100644 --- a/src/hooks/useTransactionViolationOfWorkspace.tsx +++ b/src/hooks/useTransactionViolationOfWorkspace.tsx @@ -1,4 +1,4 @@ -import {useCallback} from 'react'; +import {useCallback, useMemo} from 'react'; import type {OnyxCollection} from 'react-native-onyx'; import {extractCollectionItemID} from '@libs/CollectionUtils'; import {getReportTransactions, isChatRoom, isPolicyExpenseChat, isPolicyRelatedReport, isTaskReport} from '@libs/ReportUtils'; @@ -9,19 +9,26 @@ import useOnyx from './useOnyx'; function useTransactionViolationOfWorkspace(policyID?: string) { const [allReports] = useOnyx(ONYXKEYS.COLLECTION.REPORT, {canBeMissing: true}); - const reportsToArchive = Object.values(allReports ?? {}).filter( - (report): report is Report => report != null && isPolicyRelatedReport(report, policyID) && (isChatRoom(report) || isPolicyExpenseChat(report) || isTaskReport(report)), + const reportsToArchive = useMemo( + () => + Object.values(allReports ?? {}).filter( + (report): report is Report => report != null && isPolicyRelatedReport(report, policyID) && (isChatRoom(report) || isPolicyExpenseChat(report) || isTaskReport(report)), + ), + [allReports, policyID], ); - const transactionIDSet = new Set(); - for (const report of reportsToArchive) { - if (!report?.iouReportID) { - continue; - } - const reportTransactions = getReportTransactions(report.iouReportID); - for (const transaction of reportTransactions) { - transactionIDSet.add(transaction.transactionID); + const transactionIDSet = useMemo(() => { + const set = new Set(); + for (const report of reportsToArchive) { + if (!report?.iouReportID) { + continue; + } + const reportTransactions = getReportTransactions(report.iouReportID); + for (const transaction of reportTransactions) { + set.add(transaction.transactionID); + } } - } + return set; + }, [reportsToArchive]); const transactionViolationSelector = useCallback( (violations: OnyxCollection) => { diff --git a/src/libs/CurrencyUtils.ts b/src/libs/CurrencyUtils.ts index c8660f216dd8f..88f8cbf18fef6 100644 --- a/src/libs/CurrencyUtils.ts +++ b/src/libs/CurrencyUtils.ts @@ -8,7 +8,7 @@ import {format, formatToParts} from './NumberFormatUtils'; let currencyList: OnyxValues[typeof ONYXKEYS.CURRENCY_LIST] = {}; -Onyx.connect({ +Onyx.connectWithoutView({ key: ONYXKEYS.CURRENCY_LIST, callback: (val) => { if (!val || Object.keys(val).length === 0) { diff --git a/src/libs/LocalePhoneNumber.ts b/src/libs/LocalePhoneNumber.ts index dcef75752bc5f..0e1c9ac6a92ef 100644 --- a/src/libs/LocalePhoneNumber.ts +++ b/src/libs/LocalePhoneNumber.ts @@ -5,7 +5,7 @@ import ONYXKEYS from '@src/ONYXKEYS'; import {parsePhoneNumber} from './PhoneNumber'; let countryCodeByIPOnyx: number; -Onyx.connect({ +Onyx.connectWithoutView({ key: ONYXKEYS.COUNTRY_CODE, callback: (val) => (countryCodeByIPOnyx = val ?? 1), }); diff --git a/src/libs/Middleware/Pagination.ts b/src/libs/Middleware/Pagination.ts index 3f5c76474f19d..ac99e25630316 100644 --- a/src/libs/Middleware/Pagination.ts +++ b/src/libs/Middleware/Pagination.ts @@ -50,14 +50,14 @@ function registerPaginationConfig({ + Onyx.connectWithoutView({ key: config.resourceCollectionKey, waitForCollectionCallback: true, callback: (data) => { resources.set(config.resourceCollectionKey, data); }, }); - Onyx.connect({ + Onyx.connectWithoutView({ key: config.pageCollectionKey, waitForCollectionCallback: true, callback: (data) => { diff --git a/src/pages/workspace/companyCards/addNew/CardTypeStep.tsx b/src/pages/workspace/companyCards/addNew/CardTypeStep.tsx index 78db9a77cd15e..0da23f7d3e007 100644 --- a/src/pages/workspace/companyCards/addNew/CardTypeStep.tsx +++ b/src/pages/workspace/companyCards/addNew/CardTypeStep.tsx @@ -1,4 +1,4 @@ -import React, {useCallback, useEffect, useMemo, useState} from 'react'; +import React, {useCallback, useMemo, useState} from 'react'; import {View} from 'react-native'; import type {StyleProp, ViewStyle} from 'react-native'; import FormHelpMessage from '@components/FormHelpMessage'; @@ -83,7 +83,8 @@ function CardTypeStep() { const styles = useThemeStyles(); const companyCardBankIcons = useCompanyCardBankIcons(); const [addNewCard] = useOnyx(ONYXKEYS.ADD_NEW_COMPANY_CARD, {canBeMissing: true}); - const [typeSelected, setTypeSelected] = useState(); + const [localTypeSelected, setLocalTypeSelected] = useState(); + const typeSelected = localTypeSelected ?? addNewCard?.data.feedType; const [isError, setIsError] = useState(false); const data = getAvailableCompanyCardTypes({ translate, @@ -111,10 +112,6 @@ function CardTypeStep() { } }, [bankName, isNewCardTypeSelected, isOtherBankSelected, typeSelected]); - useEffect(() => { - setTypeSelected(addNewCard?.data.feedType); - }, [addNewCard?.data.feedType]); - const handleBackButtonPress = () => { if (isOtherBankSelected) { setAddNewCompanyCardStepAndData({step: CONST.COMPANY_CARDS.STEP.SELECT_BANK}); @@ -153,7 +150,7 @@ function CardTypeStep() { data={data} ListItem={RadioListItem} onSelectRow={({value}) => { - setTypeSelected(value); + setLocalTypeSelected(value); setIsError(false); }} confirmButtonOptions={confirmButtonOptions} diff --git a/src/pages/workspace/companyCards/addNew/SelectBankStep.tsx b/src/pages/workspace/companyCards/addNew/SelectBankStep.tsx index 5820d3bbf7e30..81ee52e34a062 100644 --- a/src/pages/workspace/companyCards/addNew/SelectBankStep.tsx +++ b/src/pages/workspace/companyCards/addNew/SelectBankStep.tsx @@ -1,5 +1,5 @@ import {useRoute} from '@react-navigation/native'; -import React, {useCallback, useEffect, useMemo, useState} from 'react'; +import React, {useCallback, useMemo, useState} from 'react'; import {View} from 'react-native'; import type {ValueOf} from 'type-fest'; import FormHelpMessage from '@components/FormHelpMessage'; @@ -36,7 +36,8 @@ function SelectBankStep() { const [addNewCard] = useOnyx(ONYXKEYS.ADD_NEW_COMPANY_CARD, {canBeMissing: true}); const [isDebugModeEnabled = false] = useOnyx(ONYXKEYS.IS_DEBUG_MODE_ENABLED, {canBeMissing: true}); - const [bankSelected, setBankSelected] = useState | null>(); + const [localBankSelected, setLocalBankSelected] = useState | null>(); + const bankSelected = localBankSelected ?? addNewCard?.data.selectedBank; const [hasError, setHasError] = useState(false); const isOtherBankSelected = bankSelected === CONST.COMPANY_CARDS.BANKS.OTHER; @@ -56,10 +57,6 @@ function SelectBankStep() { } }, [bankSelected, isOtherBankSelected]); - useEffect(() => { - setBankSelected(addNewCard?.data.selectedBank); - }, [addNewCard?.data.selectedBank]); - const handleBackButtonPress = () => { if (route?.params?.backTo) { Navigation.navigate(route.params.backTo); @@ -118,10 +115,10 @@ function SelectBankStep() { data={data} ListItem={RadioListItem} onSelectRow={({value}) => { - setBankSelected(value); + setLocalBankSelected(value); setHasError(false); }} - initiallyFocusedItemKey={addNewCard?.data.selectedBank ?? undefined} + initiallyFocusedItemKey={bankSelected ?? undefined} confirmButtonOptions={confirmButtonOptions} shouldSingleExecuteRowSelect shouldUpdateFocusedIndex diff --git a/src/pages/workspace/companyCards/addNew/SelectFeedType.tsx b/src/pages/workspace/companyCards/addNew/SelectFeedType.tsx index c253de4572fae..26249bd72e775 100644 --- a/src/pages/workspace/companyCards/addNew/SelectFeedType.tsx +++ b/src/pages/workspace/companyCards/addNew/SelectFeedType.tsx @@ -1,4 +1,4 @@ -import React, {useCallback, useEffect, useMemo, useState} from 'react'; +import React, {useCallback, useMemo, useState} from 'react'; import {View} from 'react-native'; import type {ValueOf} from 'type-fest'; import FormHelpMessage from '@components/FormHelpMessage'; @@ -20,10 +20,12 @@ function SelectFeedType() { const {translate} = useLocalize(); const styles = useThemeStyles(); const [addNewCard] = useOnyx(ONYXKEYS.ADD_NEW_COMPANY_CARD, {canBeMissing: true}); - const [typeSelected, setTypeSelected] = useState>(); + const [localTypeSelected, setLocalTypeSelected] = useState>(); const [hasError, setHasError] = useState(false); const doesCountrySupportPlaid = isPlaidSupportedCountry(addNewCard?.data?.selectedCountry); const isUSCountry = addNewCard?.data?.selectedCountry === CONST.COUNTRY.US; + const defaultTypeSelected = addNewCard?.data.selectedFeedType ?? (doesCountrySupportPlaid ? CONST.COMPANY_CARDS.FEED_TYPE.DIRECT : undefined); + const typeSelected = localTypeSelected ?? defaultTypeSelected; const submit = useCallback(() => { if (!typeSelected) { @@ -46,16 +48,6 @@ function SelectFeedType() { }); }, [isUSCountry, typeSelected]); - useEffect(() => { - if (addNewCard?.data.selectedFeedType) { - setTypeSelected(addNewCard?.data.selectedFeedType); - return; - } - if (doesCountrySupportPlaid) { - setTypeSelected(CONST.COMPANY_CARDS.FEED_TYPE.DIRECT); - } - }, [addNewCard?.data.selectedFeedType, doesCountrySupportPlaid]); - const handleBackButtonPress = () => { setAddNewCompanyCardStepAndData({step: CONST.COMPANY_CARDS.STEP.SELECT_COUNTRY}); }; @@ -118,7 +110,7 @@ function SelectFeedType() { ListItem={RadioListItem} data={finalData} onSelectRow={({value}) => { - setTypeSelected(value); + setLocalTypeSelected(value); setHasError(false); }} shouldSingleExecuteRowSelect diff --git a/src/pages/workspace/companyCards/assignCard/InviteNewMemberStep.tsx b/src/pages/workspace/companyCards/assignCard/InviteNewMemberStep.tsx index 2e416fcd32bbd..6494fd2a0c5d2 100644 --- a/src/pages/workspace/companyCards/assignCard/InviteNewMemberStep.tsx +++ b/src/pages/workspace/companyCards/assignCard/InviteNewMemberStep.tsx @@ -1,4 +1,4 @@ -import React, {useEffect} from 'react'; +import React, {useCallback, useEffect} from 'react'; import InteractiveStepWrapper from '@components/InteractiveStepWrapper'; import type {WithCurrentUserPersonalDetailsProps} from '@components/withCurrentUserPersonalDetails'; import withCurrentUserPersonalDetails from '@components/withCurrentUserPersonalDetails'; @@ -49,7 +49,7 @@ function InviteNewMemberStep({route, currentUserPersonalDetails}: InviteeNewMemb Navigation.goBack(); }; - const goToNextStep = () => { + const goToNextStep = useCallback(() => { const defaultCardName = getDefaultCardName(assignCard?.cardToAssign?.invitingMemberEmail); const cardToAssign: Partial = { email: assignCard?.cardToAssign?.invitingMemberEmail, @@ -91,7 +91,18 @@ function InviteNewMemberStep({route, currentUserPersonalDetails}: InviteeNewMemb }); Navigation.navigate(ROUTES.WORKSPACE_COMPANY_CARDS_ASSIGN_CARD_CARD_SELECTION.getRoute(routeParams), {forceReplace: true}); } - }; + }, [ + assignCard?.cardToAssign?.invitingMemberEmail, + assignCard?.cardToAssign?.startDate, + assignCard?.cardToAssign?.dateOption, + assignCard?.cardToAssign?.encryptedCardNumber, + assignCard?.cardToAssign?.cardName, + assignCard?.cardToAssign?.customCardName, + filteredCardList, + policyID, + feed, + cardID, + ]); // If the currently inviting member is already a member of the policy then we should just call goToNextStep // See https://github.com/Expensify/App/issues/74256 for more details diff --git a/src/pages/workspace/receiptPartners/InviteReceiptPartnerPolicyPage.tsx b/src/pages/workspace/receiptPartners/InviteReceiptPartnerPolicyPage.tsx index 554dea84ac0f2..faacc68ca5808 100644 --- a/src/pages/workspace/receiptPartners/InviteReceiptPartnerPolicyPage.tsx +++ b/src/pages/workspace/receiptPartners/InviteReceiptPartnerPolicyPage.tsx @@ -1,4 +1,4 @@ -import React, {useEffect, useState} from 'react'; +import React, {useEffect, useMemo, useState} from 'react'; import ConfirmationPage from '@components/ConfirmationPage'; import HeaderWithBackButton from '@components/HeaderWithBackButton'; // eslint-disable-next-line no-restricted-imports @@ -45,8 +45,11 @@ function InviteReceiptPartnerPolicyPage({route}: InviteReceiptPartnerPolicyPageP const policy = usePolicy(policyID); const shouldShowTextInput = policy?.employeeList && Object.keys(policy.employeeList).length >= CONST.STANDARD_LIST_ITEM_LIMIT; const textInputLabel = shouldShowTextInput ? translate('common.search') : undefined; - const workspaceMembers: MemberForList[] = []; - if (policy?.employeeList) { + const workspaceMembers = useMemo((): MemberForList[] => { + const members: MemberForList[] = []; + if (!policy?.employeeList) { + return members; + } // Get the list of employees from the U4B organization const uberEmployees = policy?.receiptPartners?.uber?.employees ?? {}; @@ -85,12 +88,13 @@ function InviteReceiptPartnerPolicyPage({route}: InviteReceiptPartnerPolicyPageP isSelected: true, }); - workspaceMembers.push(memberForList); + members.push(memberForList); } } - sortAlphabetically(workspaceMembers, 'text', localeCompare); - } + sortAlphabetically(members, 'text', localeCompare); + return members; + }, [policy?.employeeList, policy?.receiptPartners?.uber?.employees, isOffline, icons, localeCompare]); const allMembersWithState: MemberForList[] = []; if (workspaceMembers.length > 0) { diff --git a/src/pages/workspace/receiptPartners/WorkspaceReceiptPartnersPage.tsx b/src/pages/workspace/receiptPartners/WorkspaceReceiptPartnersPage.tsx index 15a82f8d39982..419ccec1a30bc 100644 --- a/src/pages/workspace/receiptPartners/WorkspaceReceiptPartnersPage.tsx +++ b/src/pages/workspace/receiptPartners/WorkspaceReceiptPartnersPage.tsx @@ -246,6 +246,7 @@ function WorkspaceReceiptPartnersPage({route}: WorkspaceReceiptPartnersPageProps isUberConnected, calculateAndSetThreeDotsMenuPosition, policy?.receiptPartners?.uber, + policy?.isLoadingReceiptPartners, isOffline, startIntegrationFlow, ]); diff --git a/src/pages/workspace/upgrade/WorkspaceUpgradePage.tsx b/src/pages/workspace/upgrade/WorkspaceUpgradePage.tsx index 0c15732575849..ba3f7eb84e08f 100644 --- a/src/pages/workspace/upgrade/WorkspaceUpgradePage.tsx +++ b/src/pages/workspace/upgrade/WorkspaceUpgradePage.tsx @@ -196,6 +196,7 @@ function WorkspaceUpgradePage({route}: WorkspaceUpgradePageProps) { policy?.connections?.xero?.config, policy?.connections?.xero?.data, policyID, + policyData, qboConfig?.syncClasses, qboConfig?.syncCustomers, qboConfig?.syncLocations, diff --git a/src/types/form/AddDomainMemberForm.ts b/src/types/form/AddDomainMemberForm.ts index f87e8a1670f6c..41d3e23fb5c4d 100644 --- a/src/types/form/AddDomainMemberForm.ts +++ b/src/types/form/AddDomainMemberForm.ts @@ -1,5 +1,5 @@ import type {ValueOf} from 'type-fest'; -import type Form from '@src/types/form/Form'; +import type Form from './Form'; const INPUT_IDS = { EMAIL: 'email',