You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
-**Search patterns**: Multiple `useEffect` in single component, large component bodies mixing data access/navigation/UI state/lifecycle, hooks or utilities handling several unrelated responsibilities
1195
+
1196
+
-**Condition**: Flag when a component, hook, or utility aggregates multiple unrelated responsibilities in a single unit, making it difficult to modify one concern without touching others.
1197
+
1198
+
**Signs of violation:**
1199
+
- Component has several `useEffect` hooks handling unrelated concerns (e.g., telemetry, deep linking, audio, session management all in one component)
1200
+
- A single `useEffect` or hook handles multiple distinct responsibilities
1201
+
- Unrelated state variables are interdependent or updated together
1202
+
- Logic mixes data fetching, navigation, UI state, and lifecycle behavior in one place
1203
+
- Removing one piece of functionality requires careful untangling from others
1204
+
1205
+
**What counts as "unrelated":**
1206
+
- Group by responsibility (what the code does), NOT by timing (when it runs)
1207
+
- Data fetching and analytics are NOT related — they serve different purposes even if both run on mount
1208
+
- Session management and audio configuration are NOT related — different domains entirely
1209
+
1210
+
**DO NOT flag if:**
1211
+
- Component is a thin orchestration layer that ONLY composes child components (no business logic, no effects beyond rendering)
1212
+
- Effects are extracted into focused custom hooks with single responsibilities (e.g., `useDebugShortcut`, `usePriorityMode`) — inline `useEffect` calls are a code smell and should be named hooks
1213
+
1214
+
-**Reasoning**: When multiple unrelated responsibilities are grouped into a single component, hook, or utility, if any one concern changes, then unrelated logic must be touched as well, increasing coupling, regression risk, and cognitive load. This is the single responsibility principle for React: extract small units that do very little, very well. A component with several unrelated effects is a code smell - even a single effect can benefit from extraction to something with a good name, proper description, and isolated tests.
1215
+
1216
+
**Bucketing questions for refactoring:**
1217
+
1. Does this logic need the React render loop? YES → Extract to a focused custom hook. NO → Extract out of React entirely (e.g., Onyx migration, global initialization).
1218
+
2. Does this logic need to be in this component? YES → Keep it, but use a focused hook. NO → Extract to a separate component that owns this concern.
1219
+
1220
+
**Hook granularity guidance:**
1221
+
- Group effects that serve the same purpose into one hook (e.g., all telemetry setup in `useTelemetry`)
1222
+
- Group effects that can be reused together across components
1223
+
- Don't create 15 separate single-effect hooks if 5 well-named grouped hooks make more sense
1224
+
1225
+
Good (separated concerns):
1226
+
1227
+
- Each piece of logic is extracted to a focused hook or component
1228
+
- Parent component only orchestrates what to render
1229
+
- State subscriptions in smaller components don't cause re-renders in parent
1230
+
- Component-scoped hooks can be co-located in the same directory for maintainability
1231
+
1232
+
```tsx
1233
+
function DebugMenu() {
1234
+
useDebugShortcut();
1235
+
1236
+
return (
1237
+
// Debug menu UI
1238
+
);
1239
+
}
1240
+
1241
+
function ParentComponent({ reportID }: { reportID:string }) {
- The component handles telemetry, deep linking, audio, session, navigation, splash screen, and more
1339
+
- Each concern is interleaved with others, making it hard to modify one without risking regression in another
1340
+
- Effects could be extracted to focused hooks: `useTelemetrySpans`, `useDeepLinking`, `useAudioMode`, etc.
1341
+
- Entry points don't get special treatment — extracting effects into named hooks improves clarity and makes it possible to understand what each effect does and how to safely modify it
1342
+
1343
+
---
1344
+
1345
+
### [CLEAN-REACT-PATTERNS-5] Keep state and subscriptions narrow
1346
+
1347
+
-**Search patterns**: Contexts/hooks/stores exposing large bundled objects, providers with many unrelated `useOnyx` calls, state structures mixing unrelated concerns
1348
+
1349
+
-**Condition**: Flag when a state structure (context, hook, store, or subscription) bundles unrelated concerns together, causing consumers to re-render when data they don't use changes.
1350
+
1351
+
**Signs of violation:**
1352
+
- State provider (context, hook, or store) that bundles unrelated data (e.g., navigation state + list data + cache utilities in one structure)
1353
+
- State object where properties serve different purposes and change independently
1354
+
- Multiple unrelated subscriptions (`useOnyx`, `useContext`, store selectors) aggregated into a single exposed value
1355
+
- Consumers of a state source that only use a subset of the provided values
1356
+
1357
+
**DO NOT flag if:**
1358
+
- State values are cohesive — they change together and serve the same purpose (e.g., `keyboardHeight` + `isKeyboardShown` both relate to keyboard state)
1359
+
- The state structure is intentionally designed as an aggregation point and consumers use most/all values
1360
+
- Individual `useOnyx` calls without selectors — this is covered by [PERF-11]
1361
+
1362
+
-**Reasoning**: When unrelated pieces of data are grouped into a single state structure, if an unused part changes, then all consumers re-render unnecessarily. This silently expands render scope, increases coupling, and makes performance regressions hard to detect. Structuring state around cohesive concerns ensures render scope stays predictable and changes remain local.
1363
+
1364
+
**Distinction from PERF-11**: PERF-11 addresses individual `useOnyx` selector usage. This rule addresses state structure — how multiple values are grouped and exposed to consumers via contexts, hooks, or stores.
1365
+
1366
+
**Distinction from CLEAN-REACT-PATTERNS-2**: PATTERNS-2 addresses data flow direction — parent shouldn't fetch data just to pass to children. This rule addresses how state is structured and grouped within any state provider.
1367
+
1368
+
Good (cohesive state — all values serve one purpose):
1369
+
1370
+
- All state relates to one concern (keyboard)
1371
+
- Values change together — no wasted re-renders
1372
+
- Derived state computed inline, not stored separately
1373
+
1374
+
```tsx
1375
+
typeKeyboardStateContextValue= {
1376
+
isKeyboardShown:boolean;
1377
+
isKeyboardActive:boolean;
1378
+
keyboardHeight:number;
1379
+
};
1380
+
1381
+
function KeyboardStateProvider({children}:ChildrenProps) {
internalScope: Audience is members with personal credit cards already connected to their account. Covers viewing and managing existing personal cards in the Wallet, including reimbursable settings. Does not cover adding new personal cards or company card programs.
5
6
---
6
7
8
+
# Manage personal cards in New Expensify
7
9
8
-
Tracking personal credit card expenses in Expensify is simple and flexible. Whether you're getting reimbursed or just organizing your spending, you can connect a personal card or upload transactions manually.
10
+
Expensify lets you view and manage your personal credit cards in one place, making it easier to track spending, submit reimbursable expenses, and stay organized for tax time.
11
+
12
+
If you previously connected a personal card in Expensify Classic, you can now manage that card directly in Expensify on both web and mobile.
13
+
14
+
---
15
+
16
+
## Who can manage personal cards
17
+
18
+
You can manage personal cards if you have a personal credit card that was already connected to your account. You can manage these cards on both web and mobile, alongside any company cards on your account.
19
+
20
+
---
21
+
22
+
## Where to find personal cards in the Wallet
23
+
24
+
1. Navigate to **Account > Wallet** on web or mobile.
25
+
2. Under **Assigned cards**:
26
+
- Personal cards imported from Expensify Classic will appear here.
27
+
- You'll see the card name, bank icon, and last 4 digits.
28
+
3. Tap the personal card you want to manage.
29
+
30
+
If you have both company and personal cards, you'll see them separated into **Company cards** and **Personal cards** sections.
31
+
32
+
{:width="100%"}
9
33
10
34
---
11
35
12
-
# Overview
36
+
## What you can do with personal cards
37
+
38
+
You can:
39
+
- View all personal cards imported from Expensify Classic
40
+
- Automatically import transactions from your linked personal cards
41
+
- Generate IRS-compliant eReceipts for eligible USD purchases
42
+
- Update personal card settings
43
+
- Filter expenses by card in the **Expenses** tab on the **Reports** page
44
+
45
+
---
13
46
14
-
Once available in **New Expensify**, personal card connections will allow you to:
47
+
## How to update personal card settings
15
48
16
-
-**Automatically import transactions** from your linked card
17
-
-**Manually upload CSV files** with your expense history
18
-
-**Merge imported transactions with SmartScanned receipts**
19
-
- Generate **IRS-compliant eReceipts** for eligible USD purchases
49
+
1. Navigate to **Account > Wallet** on web or mobile.
50
+
2. Under **Assigned cards**, select a personal card.
51
+
3. On the **Card details** page, you can:
52
+
- Rename the card
53
+
- Update the card to pull in new transactions (if not CSV-imported)
54
+
- Unassign the card if it's no longer needed
55
+
- Toggle **Mark transactions as reimbursable**
56
+
- The reimbursable setting applies only to **new** transactions and is turned on by default.
20
57
21
-
These features make it easy to capture personal expenses for reimbursement, tax reporting, or budgeting.
58
+
**Note:** Unassigning a personal card permanently deletes any unreported expenses or expenses on draft reports from that card.
59
+
60
+
{:width="100%"}
22
61
23
62
---
24
63
25
-
# Feature Status
64
+
# FAQ
65
+
66
+
## Can I add another personal card?
67
+
68
+
Not yet. Only cards connected to your account in Expensify Classic are available to manage in New Expensify. Support for adding new personal cards will be available in a future update.
69
+
70
+
## Can I change the reimbursable setting for past transactions?
26
71
27
-
We’re currently building the personal card connection functionality for New Expensify. This feature is not yet available but will be released soon.
72
+
No. Changes to the reimbursable setting only apply to transactions imported after the change.
28
73
29
-
Once it's live, we’ll update this article with full setup instructions, screenshots, and tips for managing your imported expenses.
74
+
## Why don’t I see my personal card in the Wallet?
30
75
31
-
**Stay tuned!** You can follow product announcements or contact Concierge for updates on availability.
76
+
Personal cards appear in the Wallet only if they were previously connected to your account in Expensify Classic. If your card doesn’t show up, it means it wasn’t connected before and isn’t available to manage yet.
0 commit comments