Fix implementation of useCurrentUserPersonalDetails#69864
Conversation
|
@shubham1206agra 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] |
|
Don't mind the jest tests @shubham1206agra, they're broken on main. |
| const session = useSession(); | ||
| const personalDetails = usePersonalDetails(); | ||
| const userAccountID = useMemo(() => session?.accountID ?? CONST.DEFAULT_NUMBER_ID, [session?.accountID]); | ||
| const [userPersonalDetails] = useOnyx(ONYXKEYS.PERSONAL_DETAILS_LIST, {selector: (allPersonalDetails) => allPersonalDetails?.[userAccountID], canBeMissing: true}); |
There was a problem hiding this comment.
@SzymczakJ Whenever we use this hook, it will create this subscription again. Can we somehow get rid of this Onyx subscription duplication?
There was a problem hiding this comment.
I was thinking about the same thing as you, but in the end I think this is the best solution.
I considered a few options to avoid using useOnyx here:
- create
Onyx.connecton the top of the file, but this option is obviously a wrong idea as we deprecate theOnyx.connectand the problems with reactivity thatOnyx.connectwas introducing. - create another context just for
userPeronalDetails. It think it's not worth it, especially when we think of how much contexts we already have in this app. What's more, useContext also has it's own overhead(as it has to look for the closest context provider, which would be really close to the root etc.) and I'm pretty sure that it's comparable to the overhead thatuseOnyxgives us. - stay with previous implementation that was using
usePersonalDetails. This option is the worst, it will rerender every component that uses it every time any personal details list changes. Obvioulsy cost of rerendering component many times will be bigger than adding a single useOnyx.
I also measured the performance of onyx internal function that notifies useOnyx subscribers(fireCallbacks) with the new implmentation and the total execution time was pretty much the same(with the new implementation it was even better but I guess it's a measurement error). So to conclude: yes I know that using many useOnyx connections is bad, but it doesn't hurt us as bad when we consider how much rerenders it saved us in this particular case.
But if you have any other idea how to stop using useOnyx subscription duplication, tell me because it would be a nice improvement.
There was a problem hiding this comment.
@SzymczakJ It would be better if we benchmark option 2 with the current implementation
There was a problem hiding this comment.
You're right, I will profile and come back to you ASAP
There was a problem hiding this comment.
It turns out that option 2 with context is much better than having useOnyx for each useCurrentUserPersonalDetails.
While the profiling outcomes for a given scenario that I was testing are pretty much the same(because the whole useCurrentUserPersonalDetails is really small), there is a big difference in the total measured time of useCurrentUserPersonalDetails.
- implementation using
useOnyxfor each hook: 2.5325 ms of overhead - implementation using context(option 2): 0.0707 ms of overhead
I've already pushed the new version.
There was a problem hiding this comment.
@SzymczakJ You need to fix the test to include this new context.
| const session = useSession(); | ||
| const personalDetails = usePersonalDetails(); | ||
| const userAccountID = useMemo(() => session?.accountID ?? CONST.DEFAULT_NUMBER_ID, [session?.accountID]); | ||
| const [userPersonalDetails] = useOnyx(ONYXKEYS.PERSONAL_DETAILS_LIST, {selector: (allPersonalDetails) => allPersonalDetails?.[userAccountID], canBeMissing: true}); |
There was a problem hiding this comment.
If you define the selector at the top level of the file (outside the hook), you'll get a stable reference to the selector for all usages of the hook. That in turn will mean that the OnyxSnapshotCache can do it's job, and we get one cache entry for all usages of this hook, rather than 1 cache entry per usage. That will lead to better performance (because the function call is cached) and a smaller memory footprint (because we won't be adding new entries to the snapshot cache for every usage of the hook).
23bc1c2 to
a5fb2e0
Compare
|
Ready for your review @shubham1206agra |
|
bump @shubham1206agra, I see you posting on open source 😄 👁️ 🔭 |
|
@SzymczakJ Can you fix the warnings? |
Reviewer Checklist
Screenshots/VideosAndroid: HybridAppAndroid: mWeb ChromeScreen.Recording.2025-09-10.at.9.11.46.PM.moviOS: HybridAppScreen.Recording.2025-09-10.at.10.32.01.PM.moviOS: mWeb SafariScreen.Recording.2025-09-10.at.9.09.35.PM.movMacOS: Chrome / SafariScreen.Recording.2025-09-10.at.9.07.17.PM.movMacOS: DesktopScreen.Recording.2025-09-10.at.9.16.32.PM.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/mountiny in version: 9.2.9-0 🚀
|
|
🚀 Deployed to staging by https://github.com/mountiny in version: 9.2.11-0 🚀
|
|
🚀 Deployed to production by https://github.com/francoisl in version: 9.2.12-4 🚀
|
| }, | ||
| [userAccountID], | ||
| ); | ||
| const [currentUserPersonalDetails = defaultCurrentUserPersonalDetails] = useOnyx(ONYXKEYS.PERSONAL_DETAILS_LIST, {selector: userAccountSelector, canBeMissing: true}); |
There was a problem hiding this comment.
We forgot to add dependencies for the selector, which caused #74176
Explanation of Change
This PR changes the implementation of
useCurrentUserPersonalDetailsby remove theusePersonalDetailshook and replacing it withuseOnyx. This stops rerenders triggered by context thatusePersonalDetailssubscribes to.Fixed Issues
$ #70056
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
MacOS: Desktop