Change Approver dialog - make options explicit & skip when single option#79468
Change Approver dialog - make options explicit & skip when single option#79468marcaaron merged 4 commits intoExpensify:mainfrom
Conversation
|
Hey, I noticed you changed If you want to automatically generate translations for other locales, an Expensify employee will have to:
Alternatively, if you are an external contributor, you can run the translation script locally with your own OpenAI API key. To learn more, try running: npx ts-node ./scripts/generateTranslations.ts --helpTypically, you'd want to translate only what you changed by running |
Codecov Report✅ Changes either increased or maintained existing code coverage, great job!
|
|
@alitoshmatov Please copy/paste the Reviewer Checklist from here into a new comment on this PR and complete it. If you have the K2 extension, you can simply click: [this button] |
| return data; | ||
| }, [translate, selectedApproverType, policy, report, currentUserDetails.accountID]); | ||
|
|
||
| useEffect(() => { |
There was a problem hiding this comment.
❌ PERF-6 (docs)
This useEffect updates state based on other state, but the value could be computed directly.
Suggested fix: Instead of using useEffect to set the default selected approver type, compute it directly:
const [selectedApproverType, setSelectedApproverType] = useState<ApproverType>(() => approverTypes.at(0)?.keyForList);Or derive it in the component body:
const defaultApproverType = selectedApproverType ?? approverTypes.at(0)?.keyForList;This ensures the value is always synchronized and avoids unnecessary re-renders.
There was a problem hiding this comment.
The useEffect is necessary due to circular dependency. approverTypes depends on selectedApproverType for the isSelected property, so we can't derive the initial value directly
| setSelectedApproverType(approverTypes.at(0)?.keyForList); | ||
| }, [approverTypes, selectedApproverType]); | ||
|
|
||
| useEffect(() => { |
There was a problem hiding this comment.
❌ PERF-8 (docs)
This useEffect automatically triggers an action based on computed state. This pattern creates unnecessary render cycles and makes the code flow less clear.
Suggested fix: Instead of using useEffect to auto-trigger the action, handle this logic directly when the data is ready. Consider:
- Check if only one option exists when rendering the component
- Call
changeApprover()directly in auseEffectthat runs only once on mount with proper guards - Or better: perform the navigation directly without showing this intermediate page when there's only one option (handle this in the parent/navigation logic)
This makes the relationship between the condition and the action clearer and avoids extra render cycles.
| return data; | ||
| }, [translate, selectedApproverType, policy, report, currentUserDetails.accountID]); | ||
|
|
||
| useEffect(() => { |
There was a problem hiding this comment.
❌ PERF-9 (docs)
These two useEffects form a chain where the first effect's state update (setSelectedApproverType) triggers the second effect. This creates complex dependencies and unnecessary renders.
Suggested fix: Combine the logic into a single effect or derive values directly:
useEffect(() => {
if (hasAutoAppliedRef.current || approverTypes.length \!== 1 || hasNavigatedToAddApproverRef.current) {
return;
}
hasAutoAppliedRef.current = true;
const firstOption = approverTypes.at(0)?.keyForList;
if (firstOption) {
setSelectedApproverType(firstOption);
// Call changeApprover in next tick or handle navigation directly
setTimeout(() => changeApprover(), 0);
}
}, [approverTypes, changeApprover]);This eliminates the chain and makes the logic flow clearer.
trjExpensify
left a comment
There was a problem hiding this comment.
GK had eyes on the parent issue for the change. 👍
Reviewer Checklist
Screenshots/VideosAndroid: HybridAppapprove-android.movAndroid: mWeb Chromeapprove-mweb.moviOS: HybridAppapprove-ios.mp4iOS: mWeb Safariapprove-safari.mp4MacOS: Chrome / Safariapprove-web.mov |
|
✋ This PR was not deployed to staging yet because QA is ongoing. It will be automatically deployed to staging after the next production release. |
|
🚀 Deployed to staging by https://github.com/marcaaron in version: 9.3.6-0 🚀
|
|
🚀 Deployed to production by https://github.com/Beamanator in version: 9.3.6-4 🚀
|
Explanation of Change
ReportChangeApproverPageheader copyFixed Issues
$ #77306
PROPOSAL: #77306 (comment)
Tests
Same as QA Steps
Offline tests
Same as QA Steps
QA Steps
Precondition:
Choose an option to change the approver for this report. (Update your workspace settings to change this permanently for all reports.)PR Author Checklist
### Fixed Issuessection aboveTestssectionOffline stepssectionQA stepssectioncanBeMissingparam foruseOnyxtoggleReportand notonIconClick)src/languages/*files and using the translation methodSTYLE.md) were followedAvatar, I verified the components usingAvatarare working as expected)StyleUtils.getBackgroundAndBorderStyle(theme.componentBG))npm run compress-svg)Avataris modified, I verified thatAvataris working as expected in all cases)Designlabel and/or tagged@Expensify/designso the design team can review the changes.ScrollViewcomponent to make it scrollable when more elements are added to the page.mainbranch was merged into this PR after a review, I tested again and verified the outcome was still expected according to theTeststeps.Screenshots/Videos
Android: Native
Android-Native.mp4
Android: mWeb Chrome
Android-mWeb.Chrome.mp4
iOS: Native
iOS-Native.mp4
iOS: mWeb Safari
iOS-mWeb.Safari.mp4
MacOS: Chrome / Safari
MacOS-Chrome.mp4