-
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
Convert SearchPage to functional component #23076
Convert SearchPage to functional component #23076
Conversation
CLA Assistant Lite bot All contributors have signed the CLA ✍️ ✅ |
I have read the CLA Document and I hereby sign the CLA |
src/pages/SearchPage.js
Outdated
userToInvite, | ||
}; | ||
} | ||
const debouncedUpdateOptions =_.debounce(updateOptions, 75); |
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.
_.debounce
returns always a new function, probably would be good to wrap it into an useCallback
for the useEffect
, so you can add it into the dependency array safely
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, the app should have the same reference across the re-renders. useCallback is necessary here.
src/pages/SearchPage.js
Outdated
this.setState({searchValue}, this.debouncedUpdateOptions); | ||
} | ||
useEffect(() => { | ||
updateOptions(); |
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 updateOptions
isn't part of the dependency array, please add a comment explaining why
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.
@gedu, wdyt about the next option - https://github.com/Expensify/App/pull/23076/files#r1267798688?
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 just realized that updateOptions
useCallback, also is recreated when the searchValue
change, I think could be some useEffects
and function duplications, would be good to update the logic, and merge where is possible
src/pages/SearchPage.js
Outdated
personalDetails, | ||
}); | ||
useEffect(() => { | ||
debouncedUpdateOptions() |
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.
why are you updating the options after every new word? You are listening to reports and personalDetails to update the options
} | ||
|
||
SearchPage.propTypes = propTypes; | ||
SearchPage.defaultProps = defaultProps; | ||
|
||
SearchPage.displayName = 'SearchPage'; |
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.
use hooks useLocalize
and useWindowDimensions
src/pages/SearchPage.js
Outdated
} | ||
useEffect(() => { | ||
updateOptions(); | ||
},[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.
I think this useEffect
should have only this function as a dependency. It is logically. Finally, it uses the same deps.
},[props.reports, props.personalDetails]) | |
},[updateOptions]) |
src/pages/SearchPage.js
Outdated
recentReports, | ||
personalDetails, | ||
}); | ||
useEffect(() => { |
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.
Could you please group all useEffects
in one place instead of having them in random places across the component.
src/pages/SearchPage.js
Outdated
class SearchPage extends Component { | ||
constructor(props) { | ||
super(props); | ||
function SearchPage(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.
Let's use prop destructuring now in order to be aligned with the style guidelines -> https://github.com/Expensify/App/blob/main/contributingGuides/STYLE.md#destructuring
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.
Also, you have to remove usage of defaultProps
and assign default values during the prop destructuring.
You don't need to do this one now, we should still use defaultProps
in JS files.
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.
Just one nitpick, LGTM!
src/pages/SearchPage.js
Outdated
reports: {}, | ||
}; | ||
function SearchPage({betas = [], personalDetails = {}, reports= {}}) { | ||
//Data for initialization (runs only on the first render) |
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.
//Data for initialization (runs only on the first render) | |
// Data for initialization (runs only on the first render) |
src/pages/SearchPage.js
Outdated
personalDetails: {}, | ||
reports: {}, | ||
}; | ||
function SearchPage({betas = [], personalDetails = {}, reports= {}}) { |
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.
function SearchPage({betas = [], personalDetails = {}, reports= {}}) { | |
function SearchPage({betas = [], personalDetails = {}, reports = {}}) { |
Super nitpick sorry XD
@Piotrfj The whole searching flow looks more complex than it needs to be, in my opinion. I know it was already like this before, but I propose the following to make it simpler and prevent a few re-renders:
This is how it would look like in the end: function SearchPage({betas = [], personalDetails = {}, reports= {}}) {
...
const [searchValue, setSearchValue] = useState('')
// Only 1 state for the 3 objects here
const [searchOptions, setSearchOptions] = useState({
activeRecentReports: initialRecentReports,
activePersonalDetails: initialPersonalDetails,
activeUserToInvite: initialUserToInvite
});
const {translate} = useLocalize();
// delete updateOptions
// delete debouncedUpdateOptions
...
// delete the debouncedUpdateOptions useEffect
...
const onChangeText = (value = '') => {
setSearchValue(value);
// `updateOptions` logic is here, with the debounce
_.debounce(() => {
const {recentReports: localRecentReports, personalDetails: localPersonalDetails, userToInvite: localUserToInvite} = OptionsListUtils.getSearchOptions(
reports,
personalDetails,
searchValue.trim(),
betas,
);
// only 1 state change here prevents rerenders
setSearchOptions({
activeUserToInvite: localUserToInvite,
activeRecentReports: localRecentReports,
activePersonalDetails: localPersonalDetails,
});
}, 75);
}
} This way when you look at |
src/pages/SearchPage.js
Outdated
} | ||
useEffect(() => { | ||
debouncedUpdateOptions() | ||
}, [searchValue, debouncedUpdateOptions]) |
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.
Actually, searchValue
not necessary here.
useEffect
has debouncedUpdateOptions
has updateOptions
has searchValue
. BTW, if you want to simplify this chain, you can pass searchValue
as an argument to updateOptions
.
}, [searchValue, debouncedUpdateOptions]) | |
}, [debouncedUpdateOptions]) |
src/pages/SearchPage.js
Outdated
}; | ||
function SearchPage({betas = [], personalDetails = {}, reports = {}}) { | ||
// Data for initialization (runs only on the first render) | ||
const {recentReports: initialRecentReports, personalDetails: initialPersonalDetails, userToInvite: initialUserToInvite} = useMemo(() => OptionsListUtils.getSearchOptions(reports, personalDetails, '', betas), []); |
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.
Linter will complain on this, so just disable this rule
const {recentReports: initialRecentReports, personalDetails: initialPersonalDetails, userToInvite: initialUserToInvite} = useMemo(() => OptionsListUtils.getSearchOptions(reports, personalDetails, '', betas), []); | |
// eslint-disable-next-line react-hooks/exhaustive-deps | |
const {recentReports: initialRecentReports, personalDetails: initialPersonalDetails, userToInvite: initialUserToInvite} = useMemo(() => OptionsListUtils.getSearchOptions(reports, personalDetails, '', betas), []); |
@thesahindia 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] |
@thesahindia let's get this tested and reviewed 🙏 🙇 |
@Piotrfj Can you also please resolve the outstanding comments on your PR? |
@marcaaron @allroundexperts please accept a pr |
@Piotrfj I'm on it today! |
@Piotrfj I still see some un-resolved comments. Can you please resolve these? |
@allroundexperts @Piotrfj You can ignore this comment, is not valid:
But @Piotrfj let's do this one:
|
@Piotrfj Can you please upload screen recordings for all the platforms? |
@allroundexperts sure, gonna provide them today |
Bump @Piotrfj |
@allroundexperts examples provided, sorry for the delay, I was actually providing them earlier but forgot to save them... |
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.
Nice changes overall. We are on a merge freeze I think for the next couple of days. So, we will wait a bit before merging. If it goes for too long please tag me back in here!
The checklist also looks like it went out of date somewhere if we can fix that in the meantime @allroundexperts |
Merge freeze is over so taking this off hold. @allroundexperts can you check my comment above? |
Hi @marcaaron! |
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.
Still testing well!
This is ready @marcaaron! |
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. Let's 🪨 🤘
✋ 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/marcaaron in version: 1.3.70-0 🚀
|
🚀 Deployed to production by https://github.com/thienlnam in version: 1.3.70-8 🚀
|
Details
Fixed Issues
$ #16251
PROPOSAL: proposal not yet created
Tests
Offline tests
QA Steps
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
Kapture.2023-08-23.at.14.57.44.mp4
Mobile Web - Chrome
Mobile Web - Safari
Desktop
Kapture.2023-08-23.at.14.57.44.mp4
iOS
Kapture.2023-08-24.at.13.12.01.mp4
Android
Kapture.2023-08-23.at.15.14.34.mp4