Skip to content

Commit eaa7969

Browse files
authored
Merge pull request #49487 from allgandalf/collaboratewithfabio
[NoQA] Fix Virtualised List Error
2 parents 8383570 + 98e60bb commit eaa7969

File tree

3 files changed

+99
-47
lines changed

3 files changed

+99
-47
lines changed

contributingGuides/STYLE.md

+45
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,9 @@
5050
- [Stateless components vs Pure Components vs Class based components vs Render Props](#stateless-components-vs-pure-components-vs-class-based-components-vs-render-props---when-to-use-what)
5151
- [Use Refs Appropriately](#use-refs-appropriately)
5252
- [Are we allowed to use [insert brand new React feature]?](#are-we-allowed-to-use-insert-brand-new-react-feature-why-or-why-not)
53+
- [Handling Scroll Issues with Nested Lists in React Native](#handling-scroll-issues-with-nested-lists-in-react-native)
54+
- [Wrong Approach (Using SelectionList)](#wrong-approach-using-selectionlist)
55+
- [Correct Approach (Using SelectionList)](#correct-approach-using-selectionlist)
5356
- [React Hooks: Frequently Asked Questions](#react-hooks-frequently-asked-questions)
5457
- [Onyx Best Practices](#onyx-best-practices)
5558
- [Collection Keys](#collection-keys)
@@ -1105,6 +1108,48 @@ There are several ways to use and declare refs and we prefer the [callback metho
11051108

11061109
We love React and learning about all the new features that are regularly being added to the API. However, we try to keep our organization's usage of React limited to the most stable set of features that React offers. We do this mainly for **consistency** and so our engineers don't have to spend extra time trying to figure out how everything is working. That said, if you aren't sure if we have adopted something, please ask us first.
11071110

1111+
1112+
## Handling Scroll Issues with Nested Lists in React Native
1113+
1114+
### Problem
1115+
1116+
When using `SelectionList` alongside other components (e.g., `Text`, `Button`), wrapping them inside a `ScrollView` can lead to alignment and performance issues. Additionally, using `ScrollView` with nested `FlatList` or `SectionList` causes the error:
1117+
1118+
> "VirtualizedLists should never be nested inside plain ScrollViews with the same orientation."
1119+
1120+
### Solution
1121+
1122+
The correct approach is avoid using `ScrollView`. You can add props like `listHeaderComponent` and `listFooterComponent` to add other components before or after the list while keeping the layout scrollable.
1123+
1124+
### Wrong Approach (Using `SelectionList`)
1125+
1126+
```jsx
1127+
<ScrollView>
1128+
<Text>Header Content</Text>
1129+
<SelectionList
1130+
sections={[{data}]}
1131+
ListItem={RadioListItem}
1132+
onSelectRow={handleSelect}
1133+
/>
1134+
<Button title="Submit" onPress={handleSubmit} />
1135+
</ScrollView>
1136+
```
1137+
1138+
### Correct Approach (Using `SelectionList`)
1139+
1140+
```jsx
1141+
<SelectionList
1142+
sections={[{item}]}
1143+
ListItem={RadioListItem}
1144+
onSelectRow={handleSelect}
1145+
listHeaderComponent={<Text>Header Content</Text>}
1146+
listFooterComponent={<Button title="Submit" onPress={handleSubmit} />}
1147+
/>
1148+
```
1149+
1150+
This ensures optimal performance and avoids layout issues.
1151+
1152+
11081153
## React Hooks: Frequently Asked Questions
11091154

11101155
### Are Hooks a Replacement for HOCs or Render Props?

src/pages/settings/Wallet/ChooseTransferAccountPage.tsx

+32-26
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,9 @@ import getBankIcon from '@components/Icon/BankIcons';
88
import * as Expensicons from '@components/Icon/Expensicons';
99
import MenuItem from '@components/MenuItem';
1010
import ScreenWrapper from '@components/ScreenWrapper';
11-
import ScrollView from '@components/ScrollView';
1211
import SelectionList from '@components/SelectionList';
1312
import RadioListItem from '@components/SelectionList/RadioListItem';
13+
import type {ListItem} from '@components/SelectionList/types';
1414
import useLocalize from '@hooks/useLocalize';
1515
import useThemeStyles from '@hooks/useThemeStyles';
1616
import {getLastFourDigits} from '@libs/BankAccountUtils';
@@ -20,10 +20,15 @@ import * as PaymentMethods from '@userActions/PaymentMethods';
2020
import CONST from '@src/CONST';
2121
import ONYXKEYS from '@src/ONYXKEYS';
2222
import ROUTES from '@src/ROUTES';
23-
import type {AccountData} from '@src/types/onyx';
23+
import type {AccountData, BankAccount} from '@src/types/onyx';
2424
import type {BankName} from '@src/types/onyx/Bank';
2525
import isLoadingOnyxValue from '@src/types/utils/isLoadingOnyxValue';
2626

27+
type BankAccountListItem = ListItem & {
28+
value?: number;
29+
bankAccount: BankAccount;
30+
};
31+
2732
function ChooseTransferAccountPage() {
2833
const [walletTransfer, walletTransferResult] = useOnyx(ONYXKEYS.WALLET_TRANSFER);
2934

@@ -54,7 +59,7 @@ function ChooseTransferAccountPage() {
5459
const [bankAccountsList] = useOnyx(ONYXKEYS.BANK_ACCOUNT_LIST);
5560
const selectedAccountID = walletTransfer?.selectedAccountID;
5661
const data = useMemo(() => {
57-
const options = Object.values(bankAccountsList ?? {}).map((bankAccount) => {
62+
const options = Object.values(bankAccountsList ?? {}).map((bankAccount): BankAccountListItem => {
5863
const bankName = (bankAccount.accountData?.additionalData?.bankName ?? '') as BankName;
5964
const bankAccountNumber = bankAccount.accountData?.accountNumber ?? '';
6065
const bankAccountID = bankAccount.accountData?.bankAccountID ?? bankAccount.methodID;
@@ -91,29 +96,30 @@ function ChooseTransferAccountPage() {
9196
title={translate('chooseTransferAccountPage.chooseAccount')}
9297
onBackButtonPress={() => Navigation.goBack(ROUTES.SETTINGS_WALLET_TRANSFER_BALANCE)}
9398
/>
94-
<ScrollView>
95-
<SelectionList
96-
sections={[{data}]}
97-
ListItem={RadioListItem}
98-
onSelectRow={(value) => {
99-
const accountType = value?.bankAccount?.accountType;
100-
const accountData = value?.bankAccount?.accountData;
101-
selectAccountAndNavigateBack(accountType, accountData);
102-
}}
103-
shouldSingleExecuteRowSelect
104-
shouldUpdateFocusedIndex
105-
initiallyFocusedOptionKey={walletTransfer?.selectedAccountID?.toString()}
106-
/>
107-
<MenuItem
108-
onPress={navigateToAddPaymentMethodPage}
109-
title={
110-
walletTransfer?.filterPaymentMethodType === CONST.PAYMENT_METHODS.PERSONAL_BANK_ACCOUNT
111-
? translate('paymentMethodList.addNewBankAccount')
112-
: translate('paymentMethodList.addNewDebitCard')
113-
}
114-
icon={Expensicons.Plus}
115-
/>
116-
</ScrollView>
99+
100+
<SelectionList
101+
sections={[{data}]}
102+
ListItem={RadioListItem}
103+
onSelectRow={(value) => {
104+
const accountType = value?.bankAccount?.accountType;
105+
const accountData = value?.bankAccount?.accountData;
106+
selectAccountAndNavigateBack(accountType, accountData);
107+
}}
108+
shouldSingleExecuteRowSelect
109+
shouldUpdateFocusedIndex
110+
initiallyFocusedOptionKey={walletTransfer?.selectedAccountID?.toString()}
111+
listFooterContent={
112+
<MenuItem
113+
onPress={navigateToAddPaymentMethodPage}
114+
title={
115+
walletTransfer?.filterPaymentMethodType === CONST.PAYMENT_METHODS.PERSONAL_BANK_ACCOUNT
116+
? translate('paymentMethodList.addNewBankAccount')
117+
: translate('paymentMethodList.addNewDebitCard')
118+
}
119+
icon={Expensicons.Plus}
120+
/>
121+
}
122+
/>
117123
</ScreenWrapper>
118124
);
119125
}

src/pages/workspace/expensifyCard/WorkspaceSettlementAccountPage.tsx

+22-21
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@ import HeaderWithBackButton from '@components/HeaderWithBackButton';
66
import Icon from '@components/Icon';
77
import getBankIcon from '@components/Icon/BankIcons';
88
import ScreenWrapper from '@components/ScreenWrapper';
9-
import ScrollView from '@components/ScrollView';
109
import SelectionList from '@components/SelectionList';
1110
import RadioListItem from '@components/SelectionList/RadioListItem';
1211
import Text from '@components/Text';
@@ -98,26 +97,28 @@ function WorkspaceSettlementAccountPage({route}: WorkspaceSettlementAccountPageP
9897
title={translate('workspace.expensifyCard.settlementAccount')}
9998
onBackButtonPress={() => Navigation.goBack(ROUTES.WORKSPACE_EXPENSIFY_CARD_SETTINGS.getRoute(policyID))}
10099
/>
101-
<ScrollView>
102-
<Text style={[styles.mh5, styles.mv4]}>{translate('workspace.expensifyCard.settlementAccountDescription')}</Text>
103-
{isUsedContinuousReconciliation && (
104-
<Text style={[styles.mh5, styles.mb6]}>
105-
<Text>{translate('workspace.expensifyCard.settlementAccountInfoPt1')}</Text>{' '}
106-
<TextLink onPress={() => Navigation.navigate(ROUTES.WORKSPACE_ACCOUNTING_RECONCILIATION_ACCOUNT_SETTINGS.getRoute(policyID, connectionParam))}>
107-
{translate('workspace.expensifyCard.reconciliationAccount')}
108-
</TextLink>{' '}
109-
<Text>{`(${CONST.MASKED_PAN_PREFIX}${getLastFourDigits(paymentBankAccountNumber)}) `}</Text>
110-
<Text>{translate('workspace.expensifyCard.settlementAccountInfoPt2')}</Text>
111-
</Text>
112-
)}
113-
<SelectionList
114-
sections={[{data}]}
115-
ListItem={RadioListItem}
116-
onSelectRow={({value}) => updateSettlementAccount(value ?? 0)}
117-
shouldSingleExecuteRowSelect
118-
initiallyFocusedOptionKey={paymentBankAccountID.toString()}
119-
/>
120-
</ScrollView>
100+
<SelectionList
101+
sections={[{data}]}
102+
ListItem={RadioListItem}
103+
onSelectRow={({value}) => updateSettlementAccount(value ?? 0)}
104+
shouldSingleExecuteRowSelect
105+
initiallyFocusedOptionKey={paymentBankAccountID.toString()}
106+
listHeaderContent={
107+
<>
108+
<Text style={[styles.mh5, styles.mv4]}>{translate('workspace.expensifyCard.settlementAccountDescription')}</Text>
109+
{isUsedContinuousReconciliation && (
110+
<Text style={[styles.mh5, styles.mb6]}>
111+
<Text>{translate('workspace.expensifyCard.settlementAccountInfoPt1')}</Text>{' '}
112+
<TextLink onPress={() => Navigation.navigate(ROUTES.WORKSPACE_ACCOUNTING_RECONCILIATION_ACCOUNT_SETTINGS.getRoute(policyID, connectionParam))}>
113+
{translate('workspace.expensifyCard.reconciliationAccount')}
114+
</TextLink>{' '}
115+
<Text>{`(${CONST.MASKED_PAN_PREFIX}${getLastFourDigits(paymentBankAccountNumber)}) `}</Text>
116+
<Text>{translate('workspace.expensifyCard.settlementAccountInfoPt2')}</Text>
117+
</Text>
118+
)}
119+
</>
120+
}
121+
/>
121122
</ScreenWrapper>
122123
</AccessOrNotFoundWrapper>
123124
);

0 commit comments

Comments
 (0)