-
Notifications
You must be signed in to change notification settings - Fork 3.5k
fix transaction highlighting on SearchMoneyRequestReportPage #66801
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
fix transaction highlighting on SearchMoneyRequestReportPage #66801
Conversation
|
@SzymczakJ Since this regression came again, can you tihnk of automated way to cover this logic? |
|
I think it's impossible to test logic for this particular fix. The bug consisted of two things:
In other words, the existing tests cover everything that can be reasonably tested |
|
Should we close this one now? |
|
I just managed to reproduce this bug, so not closing 😄 |
| }); | ||
| setTimeout(() => { | ||
| skipFirstTransactionsChange.current = false; | ||
| }, 1000); |
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.
Do we really need setTimeout here? Isn't requestAnimationFrame enough here?
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.
We should avoid using set timeout
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.
Yeah this just seems like a workaround
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.
Both setTimeout and requestAnimationFrame + new Promise are workarounds for Onyx which is not merging results of OpenReport API call in one render, but in a couple of consecutive renders. This Onyx behaviour makes so, that loading state is set to false in one render and data is set in the next render(or even later), which can cause bugs if we don't wait for it.
We have no guarantee that the requestAnimationFrame + new Promise will for 100% ensure that the Onyx command will be fully merged.
On the other hand setTimeout option is much safer, just because it waits for a longer period of time.
So in conclusion, they both are a nasty workarounds, but if we don't want this bug to come back in the future(because Onyx behaviour changes a bit), we should stick with the setTimeout.
WDYT @mountiny @Pujan92
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.
@SzymczakJ Can you think of ways that are not workarounds? or redesign this logic so we do not have to rely on workaround like this
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.
Thanks, I'm passing this to the team to investigate.
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.
If you watch report and reportMetadata objects you can spot that reportMetadata is set as loaded at least one render, before the object is hydrated.
@SzymczakJ I've read through all of the information you provided, and I'm not sure I understand what you mean.
In the SearchMoneyRequestReportPage the report and reportMetadata are two different values which are stored under two different onyx keys:
report -> ${ONYXKEYS.COLLECTION.REPORT}${reportIDFromRoute}
reportMetadata -> ${ONYXKEYS.COLLECTION.REPORT_METADATA}${reportIDFromRoute}
App/src/pages/Search/SearchMoneyRequestReportPage.tsx
Lines 44 to 45 in 6ace187
| const [report] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${reportIDFromRoute}`, {allowStaleData: true, canBeMissing: true}); | |
| const [reportMetadata = defaultReportMetadata] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT_METADATA}${reportIDFromRoute}`, {canBeMissing: true, allowStaleData: true}); |
They are loading independently. Besides that the reportMetadata has the default value.
If you want to control the report value loading from onyx, you can do it in the next way:
const [report, reportResult] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${reportIDFromRoute}`, {allowStaleData: true, canBeMissing: true});
const isReportLoadingFromOnyx = isLoadingOnyxValue(reportResult);
@SzymczakJ Let me know if I misunderstood anything 😅
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.
Yes, they are two separate values, but they are both coming from OpenReport API call. ReportMetadata in successData and report from the BE. I also can't use const [report, reportResult], because in this case the Onyx is empty, so it doesn't load from Onyx, but from the BE.
My main problem is that these two result of the same API call are applied in different renders(ReportMetadata first) which causes my loading state to hide even though it doesn't have the report yet.
Do you think it can be fixed?
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.
Okay, let me double-check it all!
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.
My main problem is that these two result of the same API call are applied in different renders(ReportMetadata first) which causes my loading state to hide even though it doesn't have the report yet.
I think I can reproduce it, the successData is applied a little bit earlier than the data returned from the API. I'm investigating why it happens
mountiny
left a comment
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.
Overall looks good, but the setTimeout seems like hack
| }); | ||
| setTimeout(() => { | ||
| skipFirstTransactionsChange.current = false; | ||
| }, 1000); |
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.
Yeah this just seems like a workaround
|
@SzymczakJ During the testing, I've noticed that the initial bug (transaction highlighting on |
|
Yes, the bug is no longer reproducible on main, but it's not reproducible only out of pure luck. The faulty Onyx behaviour stays the same. Screen.Recording.2025-07-30.at.15.16.02.movIn 1:08 we can see that reportMetadata is already merged and report is sitll an empty object, which then causes next render to show empty screen in 1:19. This is the problematic part. I followed these repro steps: #66801 (comment) |
|
Yeah @SzymczakJ, sorry for the confusion, as I've mentioned here I was able to reproduce the issue! What I've defined for now is that:
Example of the data passed to the
|
|
I think I was able to define the root cause of the issue. In the response of Inside the Onyx.update these updates are grouped into one mergeCollection update The inconsistency appears in notifying subscribers about the onyx update. So if I try to replace mergecollection calls to simple merge -> it works faster and there is no issue. |
|
Hey, unfortunately, I haven't had enough time today for investigations here, as I needed to switch to the deploy blockers investigation. I'll continue here on Monday! |
|
FYI I'm OOO for the whole next week. |
|
I've defined that the batch updates logic in the onyx was working for |
UPD: I'm investigating some flaky tests in both E/App and react-native-onyx. Trying to figure out if the problem is in the tests or in the functionality updates. |
|
UPD: Still working on testing/checking unit tests. It turns out the app tests were also affected by the changes in v2.0.128-2.0.130, which confused me a little. |
|
UPD: Opened Onyx PR for review! |
|
Hi there! I was finally unblocked for preparing the Onyx bump PR to |
|
I checked it and it seems that this bump will solve this bug right away. When the bump is merged I might refactor |
|
Unfortunately, one issue popped up: Expensify/react-native-onyx#669 (comment) |
|
UPD: I've started preparations for the new onyx PR, it's going to batch updates in case they come from one |
|
UPD: I've opened a new Onyx PR for review 🤞 |
|
UPD: I've started preparing Onyx bump PR! |
|
Sure, I will take a look today. |
|
I can confirm that the onyx issue was fixed @VickyStash 😁 |
|
I've opened bump PR for the review: #69872 |
|
Unfortunately the bump PR was reverted due to the deploy blocker Expensify/App#70184 😞 |
|
I wonder, if your PR really caused a bug or if the expense highlighting logic was based on previous useOnyx, which was not working correctly. Enjoy your holidays 😄. |
I've investigated it a little yesterday and the reason was in how |
|
@SzymczakJ can we close this PR? |

Explanation of Change
Fixed Issues
$ #66799
PROPOSAL:
Tests
Offline tests
QA Steps
Same as tests.
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))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: mWeb Chrome
iOS: Native
iOS: mWeb Safari
MacOS: Chrome / Safari
Screen.Recording.2025-07-21.at.16.19.09.mov
MacOS: Desktop