Fix: Custom memo for parent message of thread on LHN#24351
Fix: Custom memo for parent message of thread on LHN#24351dukenv0307 wants to merge 8 commits intoExpensify:mainfrom
Conversation
|
@parasharrajat 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] |
| }, | ||
| }), | ||
| )( | ||
| memo(OptionRowLHNData, (prevProps, nextProps) => { |
There was a problem hiding this comment.
Why the memo is not used at the top level? The current way it's used seems useless as we won't have access to any of the onyx props yet.
There was a problem hiding this comment.
Actually, we can have access to all of the onyx props when using memo like this( I just console.log prevProps and nextProps here and can verify that all props are available here )
Please correct me if I am wrong
There was a problem hiding this comment.
My bad on that one. I don't know what I was thinking 😅 But is there a reason/benefit to move the memo?
There was a problem hiding this comment.
Got it! Can we still add a memo for the outer component?
There was a problem hiding this comment.
@s77rt I believe that either the 'memo inside' or the 'outer' should be sufficient. I just double-checked the code, and we are only using one of the two.
There was a problem hiding this comment.
Is there any harm in adding a memo for the outer component? I think we should still use it to prevent the parent from unnecessary re-renders
There was a problem hiding this comment.
@s77rt On second thought, I think you are right, we should keep the outer memo.
Updated the PR once again :v
| memo(OptionRowLHNData, (prevProps, nextProps) => { | ||
| const prevParentReportActions = prevProps.parentReportActions[prevProps.fullReport.parentReportActionID]; | ||
| const nextParentReportActions = nextProps.parentReportActions[nextProps.fullReport.parentReportActionID]; | ||
| return prevParentReportActions === nextParentReportActions && _.isEqual(_.omit(prevProps, 'parentReportActions'), _.omit(nextProps, 'parentReportActions')); |
There was a problem hiding this comment.
For readability I think it would better to write this as
if (prevParentReportActions !== nextParentReportActions) {
return false;
}
return _.isEqual(_.omit(prevProps, 'parentReportActions'), _.omit(nextProps, 'parentReportActions'));| const prevParentReportActions = prevProps.parentReportActions[prevProps.fullReport.parentReportActionID]; | ||
| const nextParentReportActions = nextProps.parentReportActions[nextProps.fullReport.parentReportActionID]; | ||
| return prevParentReportActions === nextParentReportActions && _.isEqual(_.omit(prevProps, 'parentReportActions'), _.omit(nextProps, 'parentReportActions')); |
There was a problem hiding this comment.
Can we do the same for policies, I think we only care about one policy (fullReport.policyID)
There was a problem hiding this comment.
Added. Please help check again
| memo(OptionRowLHNData, (prevProps, nextProps) => { | ||
| const prevParentReportActions = prevProps.parentReportActions[prevProps.fullReport.parentReportActionID]; | ||
| const nextParentReportActions = nextProps.parentReportActions[nextProps.fullReport.parentReportActionID]; | ||
| return prevParentReportActions === nextParentReportActions && _.isEqual(_.omit(prevProps, 'parentReportActions'), _.omit(nextProps, 'parentReportActions')); |
There was a problem hiding this comment.
Instead of using _.isEqual which performs a deep compare, can we use Object.is as that's the default that memo uses.
| key: ONYXKEYS.COLLECTION.POLICY, | ||
| }, | ||
| }), | ||
| withOnyx({ |
There was a problem hiding this comment.
Let's add a comment here explaining why two different subscriptions are needed.
| if (prevParentReportActions !== nextParentReportActions || prevPolicy !== nextPolicy) { | ||
| return false; | ||
| } |
There was a problem hiding this comment.
NAB, but splitting this into two if conditions is more readable
| }, | ||
| }), | ||
| )( | ||
| memo(OptionRowLHNData, (prevProps, nextProps) => { |
There was a problem hiding this comment.
Got it! Can we still add a memo for the outer component?
| if (prevParentReportActions !== nextParentReportActions || prevPolicy !== nextPolicy) { | ||
| return false; | ||
| } | ||
| return _.omit(prevProps, 'parentReportActions', 'policies') === _.omit(nextProps, 'parentReportActions', 'policies'); |
There was a problem hiding this comment.
We should be using Object.is and not ===. Object.is should be applied to each prop. It may be easier to just use _.isEqual.
There was a problem hiding this comment.
@s77rt Sorry, I am a little bit confused. So your suggestion is using Object.is or .isEqual?
|
Oops I guess I shouldn't have approved 😅 @puneetlath This is a follow up for #23777 (comment). @parasharrajat Would you please complete the checklist here |
| const prevParentReportActions = prevProps.parentReportActions[prevProps.fullReport.parentReportActionID]; | ||
| const nextParentReportActions = nextProps.parentReportActions[nextProps.fullReport.parentReportActionID]; |
There was a problem hiding this comment.
is there a possibility that parentReportActions is null or undefined?
There was a problem hiding this comment.
Yes, parentReportActions can be undefined
There was a problem hiding this comment.
Then won't this line throw the error as we are trying to access properties on a undefined value.
|
I am thinking. Can this memo hurt app performance? DeepEqual check is costly |
|
@parasharrajat I agree with you. DeepEqual in this case can be a really expensive operation since many deep layers have to be compared |
|
@parasharrajat I wonder if any further action should we do to address the issue |
|
I don't think this PR is urgent or blocking anything. I will like to do some performance testing on this before we move forward. Unfortunately, I don't have much experience in this area so It can take some time for me to get to that point. If you think that we should merge this PR quickly, let me know. |
|
Update: I don't think we should merge this PR. Memo on each LHN row will become expensive quite easily. Onyx does not give immutable data so deep comparison is really expensive. I think we are fine with one-row updates on each report action. Anyways, we do update this row for updating the last message, etc. on each action. |
|
We will also be performing various optimizations on the app thus if this is a problem, it will be taken care of. Thanks, @dukenv0307 @s77rt for creating this PR. Appreciate your efforts. |
|
@dukenv0307 Can you close this please? |

Details
Create memo custom comparator mentioned in this discussion: #23777 (comment)
Fixed Issues
$ #23424
PROPOSAL: #23424 (comment)
Tests
Offline tests
QA Steps
PR Author Checklist
### Fixed Issuessection aboveTestssectionOffline stepssectionQA stepssectiontoggleReportand notonIconClick)myBool && <MyComponent />.src/languages/*files and using the translation methodWaiting for Copylabel for a copy review on the original GH to get the correct copy.STYLE.md) were followedAvatar, I verified the components usingAvatarare working as expected)/** comment above it */thisproperly so there are no scoping issues (i.e. foronClick={this.submit}the methodthis.submitshould be bound tothisin the constructor)thisare necessary to be bound (i.e. avoidthis.submit = this.submit.bind(this);ifthis.submitis never passed to a component event handler likeonClick)StyleUtils.getBackgroundAndBorderStyle(themeColors.componentBG))Avataris modified, I verified thatAvataris working as expected in all cases)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
Web
web.mov
Mobile Web - Chrome
chrome.mov
Mobile Web - Safari
safari.1.mp4
Desktop
desktop.mov
iOS
ios.1.mp4
Android
android.2.mp4