-
Notifications
You must be signed in to change notification settings - Fork 3.7k
Improved initial value in useRefs - changed useRefs to more suitable hooks #29643
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
5b12a46
b0be5dd
10952f2
c1a1acf
8feada1
7e06bd7
46170f8
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,9 @@ | ||
| import {useState} from 'react'; | ||
|
|
||
| // In some places we set initial value on first render, but we don't want to re-run the function | ||
| // This hook will memoize the initial value and return that without setter, so it's never changed | ||
| // https://github.com/Expensify/App/pull/29643#issuecomment-1765894078 | ||
| export default function useInitialValue<T>(initialStateFunc: () => T) { | ||
| const [initialValue] = useState(initialStateFunc); | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Shouldn't we call the
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I don't see it being called anywhere.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @allroundexperts I'm passing function to
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Nice. I didn't know that. Thanks for the link! |
||
| return initialValue; | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -238,7 +238,7 @@ function PopoverReportActionContextMenu(_props, ref) { | |
| Report.deleteReportComment(reportIDRef.current, reportActionRef.current); | ||
| } | ||
| setIsDeleteCommentConfirmModalVisible(false); | ||
| }, [reportActionRef]); | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why are we making this change?
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
| }, []); | ||
|
|
||
| const hideDeleteModal = () => { | ||
| callbackWhenDeleteModalHide.current = () => (onCancelDeleteModal.current = runAndResetCallback(onCancelDeleteModal.current)); | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -27,6 +27,7 @@ import useNetwork from '../../../hooks/useNetwork'; | |
| import useWindowDimensions from '../../../hooks/useWindowDimensions'; | ||
| import {iouPropTypes, iouDefaultProps} from '../propTypes'; | ||
| import * as Expensicons from '../../../components/Icon/Expensicons'; | ||
| import useInitialValue from '../../../hooks/useInitialValue'; | ||
|
|
||
| const propTypes = { | ||
| /** React Navigation route */ | ||
|
|
@@ -63,10 +64,10 @@ function MoneyRequestConfirmPage(props) { | |
| const {isOffline} = useNetwork(); | ||
| const {windowWidth} = useWindowDimensions(); | ||
| const prevMoneyRequestId = useRef(props.iou.id); | ||
| const iouType = useRef(lodashGet(props.route, 'params.iouType', '')); | ||
| const isDistanceRequest = MoneyRequestUtils.isDistanceRequest(iouType.current, props.selectedTab); | ||
| const iouType = useInitialValue(() => lodashGet(props.route, 'params.iouType', '')); | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What's the actual performance benefit here? Object property lookup is not expensive. Or is this just for consistency?
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. As you wrote - there is negligible performance benefit, so it's more about:
|
||
| const reportID = useInitialValue(() => lodashGet(props.route, 'params.reportID', '')); | ||
| const isDistanceRequest = MoneyRequestUtils.isDistanceRequest(iouType, props.selectedTab); | ||
| const isScanRequest = MoneyRequestUtils.isScanRequest(props.selectedTab); | ||
| const reportID = useRef(lodashGet(props.route, 'params.reportID', '')); | ||
| const [receiptFile, setReceiptFile] = useState(); | ||
| const participants = useMemo( | ||
| () => | ||
|
|
@@ -77,7 +78,7 @@ function MoneyRequestConfirmPage(props) { | |
| [props.iou.participants, props.personalDetails], | ||
| ); | ||
| const isPolicyExpenseChat = useMemo(() => ReportUtils.isPolicyExpenseChat(ReportUtils.getRootParentReport(props.report)), [props.report]); | ||
| const isManualRequestDM = props.selectedTab === CONST.TAB.MANUAL && iouType.current === CONST.IOU.TYPE.REQUEST; | ||
| const isManualRequestDM = props.selectedTab === CONST.TAB.MANUAL && iouType === CONST.IOU.TYPE.REQUEST; | ||
|
|
||
| useEffect(() => { | ||
| IOU.resetMoneyRequestCategory(); | ||
|
|
@@ -101,47 +102,47 @@ function MoneyRequestConfirmPage(props) { | |
| } | ||
| FileUtils.readFileAsync(props.iou.receiptPath, props.iou.receiptFilename).then((file) => { | ||
| if (!file) { | ||
| Navigation.goBack(ROUTES.MONEY_REQUEST.getRoute(iouType.current, reportID.current)); | ||
| Navigation.goBack(ROUTES.MONEY_REQUEST.getRoute(iouType, reportID)); | ||
| } else { | ||
| const receipt = file; | ||
| receipt.state = file && isManualRequestDM ? CONST.IOU.RECEIPT_STATE.OPEN : CONST.IOU.RECEIPT_STATE.SCANREADY; | ||
| setReceiptFile(receipt); | ||
| } | ||
| }); | ||
| }, [props.iou.receiptPath, props.iou.receiptFilename, isManualRequestDM]); | ||
| }, [props.iou.receiptPath, props.iou.receiptFilename, isManualRequestDM, iouType, reportID]); | ||
|
|
||
| useEffect(() => { | ||
| // ID in Onyx could change by initiating a new request in a separate browser tab or completing a request | ||
| if (!isDistanceRequest && prevMoneyRequestId.current !== props.iou.id) { | ||
| // The ID is cleared on completing a request. In that case, we will do nothing. | ||
| if (props.iou.id) { | ||
| Navigation.goBack(ROUTES.MONEY_REQUEST.getRoute(iouType.current, reportID.current), true); | ||
| Navigation.goBack(ROUTES.MONEY_REQUEST.getRoute(iouType, reportID), true); | ||
| } | ||
| return; | ||
| } | ||
|
|
||
| // Reset the money request Onyx if the ID in Onyx does not match the ID from params | ||
| const moneyRequestId = `${iouType.current}${reportID.current}`; | ||
| const moneyRequestId = `${iouType}${reportID}`; | ||
| const shouldReset = !isDistanceRequest && props.iou.id !== moneyRequestId; | ||
| if (shouldReset) { | ||
| IOU.resetMoneyRequestInfo(moneyRequestId); | ||
| } | ||
|
|
||
| if (_.isEmpty(props.iou.participants) || (props.iou.amount === 0 && !props.iou.receiptPath && !isDistanceRequest) || shouldReset || ReportUtils.isArchivedRoom(props.report)) { | ||
| Navigation.goBack(ROUTES.MONEY_REQUEST.getRoute(iouType.current, reportID.current), true); | ||
| Navigation.goBack(ROUTES.MONEY_REQUEST.getRoute(iouType, reportID), true); | ||
| } | ||
|
|
||
| return () => { | ||
| prevMoneyRequestId.current = props.iou.id; | ||
| }; | ||
| }, [props.iou.participants, props.iou.amount, props.iou.id, props.iou.receiptPath, isDistanceRequest, props.report]); | ||
| }, [props.iou.participants, props.iou.amount, props.iou.id, props.iou.receiptPath, isDistanceRequest, props.report, iouType, reportID]); | ||
|
|
||
| const navigateBack = () => { | ||
| let fallback; | ||
| if (reportID.current) { | ||
| fallback = ROUTES.MONEY_REQUEST.getRoute(iouType.current, reportID.current); | ||
| if (reportID) { | ||
| fallback = ROUTES.MONEY_REQUEST.getRoute(iouType, reportID); | ||
| } else { | ||
| fallback = ROUTES.MONEY_REQUEST_PARTICIPANTS.getRoute(iouType.current); | ||
| fallback = ROUTES.MONEY_REQUEST_PARTICIPANTS.getRoute(iouType); | ||
| } | ||
| Navigation.goBack(fallback); | ||
| }; | ||
|
|
@@ -211,8 +212,8 @@ function MoneyRequestConfirmPage(props) { | |
| const trimmedComment = props.iou.comment.trim(); | ||
|
|
||
| // If we have a receipt let's start the split bill by creating only the action, the transaction, and the group DM if needed | ||
| if (iouType.current === CONST.IOU.TYPE.SPLIT && props.iou.receiptPath) { | ||
| const existingSplitChatReportID = CONST.REGEX.NUMBER.test(reportID.current) ? reportID.current : ''; | ||
| if (iouType === CONST.IOU.TYPE.SPLIT && props.iou.receiptPath) { | ||
| const existingSplitChatReportID = CONST.REGEX.NUMBER.test(reportID) ? reportID : ''; | ||
| FileUtils.readFileAsync(props.iou.receiptPath, props.iou.receiptFilename).then((receipt) => { | ||
| IOU.startSplitBill( | ||
| selectedParticipants, | ||
|
|
@@ -228,7 +229,7 @@ function MoneyRequestConfirmPage(props) { | |
|
|
||
| // IOUs created from a group report will have a reportID param in the route. | ||
| // Since the user is already viewing the report, we don't need to navigate them to the report | ||
| if (iouType.current === CONST.IOU.TYPE.SPLIT && CONST.REGEX.NUMBER.test(reportID.current)) { | ||
| if (iouType === CONST.IOU.TYPE.SPLIT && CONST.REGEX.NUMBER.test(reportID)) { | ||
| IOU.splitBill( | ||
| selectedParticipants, | ||
| props.currentUserPersonalDetails.login, | ||
|
|
@@ -237,13 +238,13 @@ function MoneyRequestConfirmPage(props) { | |
| trimmedComment, | ||
| props.iou.currency, | ||
| props.iou.category, | ||
| reportID.current, | ||
| reportID, | ||
| ); | ||
| return; | ||
| } | ||
|
|
||
| // If the request is created from the global create menu, we also navigate the user to the group report | ||
| if (iouType.current === CONST.IOU.TYPE.SPLIT) { | ||
| if (iouType === CONST.IOU.TYPE.SPLIT) { | ||
| IOU.splitBillAndOpenReport( | ||
| selectedParticipants, | ||
| props.currentUserPersonalDetails.login, | ||
|
|
@@ -281,6 +282,8 @@ function MoneyRequestConfirmPage(props) { | |
| requestMoney, | ||
| createDistanceRequest, | ||
| receiptFile, | ||
| iouType, | ||
| reportID, | ||
| ], | ||
| ); | ||
|
|
||
|
|
@@ -312,11 +315,11 @@ function MoneyRequestConfirmPage(props) { | |
| return props.translate('common.distance'); | ||
| } | ||
|
|
||
| if (iouType.current === CONST.IOU.TYPE.SPLIT) { | ||
| if (iouType === CONST.IOU.TYPE.SPLIT) { | ||
| return props.translate('iou.split'); | ||
| } | ||
|
|
||
| if (iouType.current === CONST.IOU.TYPE.SEND) { | ||
| if (iouType === CONST.IOU.TYPE.SEND) { | ||
| return props.translate('common.send'); | ||
| } | ||
|
|
||
|
|
@@ -339,13 +342,13 @@ function MoneyRequestConfirmPage(props) { | |
| { | ||
| icon: Expensicons.Receipt, | ||
| text: props.translate('receipt.addReceipt'), | ||
| onSelected: () => Navigation.navigate(ROUTES.MONEY_REQUEST_RECEIPT.getRoute(iouType.current, reportID.current)), | ||
| onSelected: () => Navigation.navigate(ROUTES.MONEY_REQUEST_RECEIPT.getRoute(iouType, reportID)), | ||
| }, | ||
| ]} | ||
| /> | ||
| <MoneyRequestConfirmationList | ||
| transactionID={props.iou.transactionID} | ||
| hasMultipleParticipants={iouType.current === CONST.IOU.TYPE.SPLIT} | ||
| hasMultipleParticipants={iouType === CONST.IOU.TYPE.SPLIT} | ||
| selectedParticipants={participants} | ||
| iouAmount={props.iou.amount} | ||
| iouComment={props.iou.comment} | ||
|
|
@@ -367,15 +370,15 @@ function MoneyRequestConfirmPage(props) { | |
| }} | ||
| receiptPath={props.iou.receiptPath} | ||
| receiptFilename={props.iou.receiptFilename} | ||
| iouType={iouType.current} | ||
| reportID={reportID.current} | ||
| iouType={iouType} | ||
| reportID={reportID} | ||
| isPolicyExpenseChat={isPolicyExpenseChat} | ||
| // The participants can only be modified when the action is initiated from directly within a group chat and not the floating-action-button. | ||
| // This is because when there is a group of people, say they are on a trip, and you have some shared expenses with some of the people, | ||
| // but not all of them (maybe someone skipped out on dinner). Then it's nice to be able to select/deselect people from the group chat bill | ||
| // split rather than forcing the user to create a new group, just for that expense. The reportID is empty, when the action was initiated from | ||
| // the floating-action-button (since it is something that exists outside the context of a report). | ||
| canModifyParticipants={!_.isEmpty(reportID.current)} | ||
| canModifyParticipants={!_.isEmpty(reportID)} | ||
| policyID={props.report.policyID} | ||
| bankAccountRoute={ReportUtils.getBankAccountRoute(props.report)} | ||
| iouMerchant={props.iou.merchant} | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There's bad regression on this line:
Screen.Recording.2023-10-27.at.1.46.22.PM.mov