-
Notifications
You must be signed in to change notification settings - Fork 2.9k
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
Migrate NewChatPage to functional component #20170
Migrate NewChatPage to functional component #20170
Conversation
@stitesExpensify 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] |
); | ||
const isOptionsDataReady = ReportUtils.isReportDataReady() && OptionsListUtils.isPersonalDetailsReady(props.personalDetails); | ||
|
||
const sections = useMemo(() => { |
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.
I used useMemo
here as it was also used here -
const sections = useMemo(() => { |
But I propose to remove it following the guidelines in React docs -
Optimizing with useMemo is only valuable in a few cases:
- The calculation you’re putting in useMemo is noticeably slow, and its dependencies rarely change.
- You pass it as a prop to a component wrapped in memo. You want to skip re-rendering if the value hasn’t changed. Memoization lets your component re-render only when dependencies aren’t the same.
- The value you’re passing is later used as a dependency of some Hook. For example, maybe another useMemo calculation value depends on it. Or maybe you are depending on this value from useEffect.
2 and 3 are not applicable here. For the first point, the dependencies change often in our case which would anyway recalculate the value.
If you agree then we can remove it from TaskAssigneeSelectorModal
too because another issue there is that it uses props
as a dependency when it only uses props.translate during calculation -
}, [filteredCurrentUserOption, filteredPersonalDetails, filteredRecentReports, filteredUserToInvite, props]); |
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.
discussion on useCallback, useMemo in slack
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.
@aimane-chnaif I will remove useMemo here if you agree.
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.
I think so. I don't see much benefit of performance improvement using useMemo
here.
src/pages/NewChatPage.js
Outdated
// 1. searchTerm - when searchTerm changes updateOptionsWithSearchTerm is called by OptionsSelector's onChangeText prop | ||
// 2. updateOptionsWithSearchTerm - it will change its reference upon each rerender unnecessarily | ||
// eslint-disable-next-line react-hooks/exhaustive-deps | ||
}, [props.reports, props.personalDetails]); |
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.
Only added these two dependencies based on the code in componentDidUpdate
.
Reassigning due to #16239 (comment) |
src/pages/NewChatPage.js
Outdated
|
||
updateOptionsWithSearchTerm(searchTerm = '') { | ||
const updateOptionsWithSearchTerm = (newSearchTerm = '') => { |
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.
I didn't wrap it in useCallback
based on the guidelines in React docs -
Caching a function with useCallback is only valuable in a few cases:
- You pass it as a prop to a component wrapped in memo. You want to skip re-rendering if the value hasn’t changed. Memoization lets your component re-render only if dependencies changed.
- The function you’re passing is later used as a dependency of some Hook. For example, another function wrapped in useCallback depends on it, or you depend on this function from useEffect.
The first point is NA as OptionsSelector
is not wrapped in memo
so it will change anyway. The second point is NA since it is not a dependency of any hook.
Another reason is if we wrap it in useCallback
then excludedGroupEmails
has to be added as a dependency and it is not a state variable so it will refer to a new object in memory upon each rerender which would need another hook for memoization thus I think it's best not to add.
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.
Please use function
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.
@aimane-chnaif if I use function, eslint throws error -
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.
saw your comment on slack
I will update the PR and only use function
keyword when the function is not a prop.
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.
didn't use function here as it was passed down as prop
*/ | ||
getSections(maxParticipantsReached) { | ||
const sections = []; | ||
function NewChatPage(props) { |
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.
using function based on the discussion here
Merge conflicts and need to run prettier Slight bump @aimane-chnaif |
@thienlnam fixed both. |
reviewing now |
@aimane-chnaif friendly bump :) |
src/pages/NewChatPage.js
Outdated
|
||
/** | ||
* Creates a new 1:1 chat with the option and the current user, | ||
* or navigates to the existing chat if one with those participants already exists. | ||
* | ||
* @param {Object} option | ||
*/ | ||
createChat(option) { | ||
const createChat = (option) => { |
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.
Same here
|
||
/** | ||
* Creates a new group chat with all the selected options and the current user, | ||
* or navigates to the existing chat if one with those participants already exists. | ||
*/ | ||
createGroup() { | ||
if (!this.props.isGroupChat) { | ||
const createGroup = () => { |
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.
Same 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.
didn't use function here as it was passed down as prop
src/pages/NewChatPage.js
Outdated
toggleOption(option) { | ||
this.setState((prevState) => { | ||
const isOptionInList = _.some(prevState.selectedOptions, (selectedOption) => selectedOption.login === option.login); | ||
const toggleOption = (option) => { |
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.
Same here
Hey @aimane-chnaif, updated the PR as per your feedback. |
@Nikhil-Vats lint failing |
@aimane-chnaif fixed. |
I don't see any useCallback, useMemo for optimization. |
|
@aimane-chnaif |
@Nikhil-Vats please check
We can avoid
|
ok so as you said here that useMemo won't be very useful here. For useCallback, react docs says that -
None of these is applicable here that's why I didn't wrap it in What do you think? Should I add useMemo/useCallback? |
@thienlnam what is your suggestion of whether to use useCallback/useMemo or not? |
Let's go with useMemo - yes there are only a few cases where it makes a difference and there might not be a huge difference here, but this case applies
and so we don't want it to continually recalculate on each re-render |
@aimane-chnaif @thienlnam added |
Reviewer Checklist
Screenshots/VideosWebweb.movMobile Web - Chromemchrome.movMobile Web - Safarimsafari.movDesktopdesktop.moviOSios.movAndroidandroid.movandroid2.mov |
I think we can remove
Here, the dependencies excluded are
And define this outside of component lifecycle:
|
@aimane-chnaif PR updated as per your feedback. |
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.
LGTM 🎉
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.
Awesome, thank you!
✋ 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/thienlnam in version: 1.3.37-0 🚀
|
🚀 Deployed to production by https://github.com/Julesssss in version: 1.3.37-7 🚀
|
Details
Migrate NewChatPage to functional component
Fixed Issues
$ #16239
Tests
Individual chat -
Group chat -
Offline tests
Follow the same steps as above but in offline mode.
QA Steps
Follow the same steps listed in the sections above.
PR Author Checklist
### Fixed Issues
section aboveTests
sectionOffline steps
sectionQA steps
sectiontoggleReport
and notonIconClick
)myBool && <MyComponent />
.src/languages/*
files and using the translation methodWaiting for Copy
label for a copy review on the original GH to get the correct copy.STYLE.md
) were followedAvatar
, I verified the components usingAvatar
are working as expected)/** comment above it */
this
properly so there are no scoping issues (i.e. foronClick={this.submit}
the methodthis.submit
should be bound tothis
in the constructor)this
are necessary to be bound (i.e. avoidthis.submit = this.submit.bind(this);
ifthis.submit
is never passed to a component event handler likeonClick
)StyleUtils.getBackgroundAndBorderStyle(themeColors.componentBG)
)Avatar
is modified, I verified thatAvatar
is working as expected in all cases)ScrollView
component to make it scrollable when more elements are added to the page.main
branch was merged into this PR after a review, I tested again and verified the outcome was still expected according to theTest
steps.Screenshots/Videos
Web
web.mov
Mobile Web - Chrome
mWeb.chrome.mov
Mobile Web - Safari
mWeb.safari.mov
Desktop
desktop.mov
iOS
ios.mov
ios.offline.mov
Android
android1.mov
android2.mov