diff --git a/src/pages/SearchPage.js b/src/pages/SearchPage.js
index 01fe83289d71..d779ab856fb5 100755
--- a/src/pages/SearchPage.js
+++ b/src/pages/SearchPage.js
@@ -1,17 +1,14 @@
import PropTypes from 'prop-types';
-import React, {Component} from 'react';
+import React, {useCallback, useEffect, useMemo, useRef, useState} from 'react';
import {View} from 'react-native';
import {withOnyx} from 'react-native-onyx';
import _ from 'underscore';
import HeaderWithBackButton from '@components/HeaderWithBackButton';
-import networkPropTypes from '@components/networkPropTypes';
-import {withNetwork} from '@components/OnyxProvider';
import OptionsSelector from '@components/OptionsSelector';
import ScreenWrapper from '@components/ScreenWrapper';
-import withLocalize, {withLocalizePropTypes} from '@components/withLocalize';
-import withThemeStyles, {withThemeStylesPropTypes} from '@components/withThemeStyles';
-import withWindowDimensions, {windowDimensionsPropTypes} from '@components/withWindowDimensions';
-import compose from '@libs/compose';
+import useLocalize from '@hooks/useLocalize';
+import useNetwork from '@hooks/useNetwork';
+import useThemeStyles from '@hooks/useThemeStyles';
import Navigation from '@libs/Navigation/Navigation';
import * as OptionsListUtils from '@libs/OptionsListUtils';
import Performance from '@libs/Performance';
@@ -35,207 +32,195 @@ const propTypes = {
/** All reports shared with the user */
reports: PropTypes.objectOf(reportPropTypes),
- /** Window Dimensions Props */
- ...windowDimensionsPropTypes,
-
- ...withLocalizePropTypes,
-
- /** Network info */
- network: networkPropTypes,
-
/** Whether we are searching for reports in the server */
isSearchingForReports: PropTypes.bool,
- ...withThemeStylesPropTypes,
};
const defaultProps = {
betas: [],
personalDetails: {},
reports: {},
- network: {},
isSearchingForReports: false,
};
-class SearchPage extends Component {
- constructor(props) {
- super(props);
+function SearchPage({betas, personalDetails, reports, isSearchingForReports}) {
+ const [searchValue, setSearchValue] = useState('');
+ const [searchOptions, setSearchOptions] = useState(() => {
+ const {
+ recentReports: localRecentReports,
+ personalDetails: localPersonalDetails,
+ userToInvite: localUserToInvite,
+ } = OptionsListUtils.getSearchOptions(reports, personalDetails, '', betas);
+
+ return {
+ recentReports: localRecentReports,
+ personalDetails: localPersonalDetails,
+ userToInvite: localUserToInvite,
+ };
+ });
+
+ const {isOffline} = useNetwork();
+ const {translate} = useLocalize();
+ const themeStyles = useThemeStyles();
+ const isMounted = useRef(false);
+
+ const updateOptions = useCallback(() => {
+ const {
+ recentReports: localRecentReports,
+ personalDetails: localPersonalDetails,
+ userToInvite: localUserToInvite,
+ } = OptionsListUtils.getSearchOptions(reports, personalDetails, searchValue.trim(), betas);
+
+ setSearchOptions({
+ recentReports: localRecentReports,
+ personalDetails: localPersonalDetails,
+ userToInvite: localUserToInvite,
+ });
+ }, [reports, personalDetails, searchValue, betas]);
+
+ const debouncedUpdateOptions = useMemo(() => _.debounce(updateOptions, 75), [updateOptions]);
+ useEffect(() => {
Timing.start(CONST.TIMING.SEARCH_RENDER);
Performance.markStart(CONST.TIMING.SEARCH_RENDER);
+ }, []);
- this.searchRendered = this.searchRendered.bind(this);
- this.selectReport = this.selectReport.bind(this);
- this.onChangeText = this.onChangeText.bind(this);
- this.updateOptions = this.updateOptions.bind(this);
- this.debouncedUpdateOptions = _.debounce(this.updateOptions.bind(this), 75);
- this.state = {
- searchValue: '',
- recentReports: {},
- personalDetails: {},
- userToInvite: {},
- };
- }
+ useEffect(() => {
+ updateOptions();
+ }, [reports, personalDetails, betas, updateOptions]);
- componentDidUpdate(prevProps) {
- if (_.isEqual(prevProps.reports, this.props.reports) && _.isEqual(prevProps.personalDetails, this.props.personalDetails)) {
+ useEffect(() => {
+ if (!isMounted.current) {
+ isMounted.current = true;
return;
}
- this.updateOptions();
- }
- onChangeText(searchValue = '') {
- Report.searchInServer(searchValue);
- this.setState({searchValue}, this.debouncedUpdateOptions);
- }
+ debouncedUpdateOptions();
+ // Ignoring the rule intentionally, we want to run the code only when search Value changes to prevent additional runs.
+ // eslint-disable-next-line react-hooks/exhaustive-deps
+ }, [searchValue]);
/**
* Returns the sections needed for the OptionsSelector
*
* @returns {Array}
*/
- getSections() {
+ const getSections = () => {
const sections = [];
let indexOffset = 0;
- if (this.state.recentReports.length > 0) {
+ if (searchOptions.recentReports.length > 0) {
sections.push({
- data: this.state.recentReports,
+ data: searchOptions.recentReports,
shouldShow: true,
indexOffset,
});
- indexOffset += this.state.recentReports.length;
+ indexOffset += searchOptions.recentReports.length;
}
- if (this.state.personalDetails.length > 0) {
+ if (searchOptions.personalDetails.length > 0) {
sections.push({
- data: this.state.personalDetails,
+ data: searchOptions.personalDetails,
shouldShow: true,
indexOffset,
});
- indexOffset += this.state.recentReports.length;
+ indexOffset += searchOptions.recentReports.length;
}
- if (this.state.userToInvite) {
+ if (searchOptions.userToInvite) {
sections.push({
- data: [this.state.userToInvite],
+ data: [searchOptions.userToInvite],
shouldShow: true,
indexOffset,
});
}
return sections;
- }
+ };
- searchRendered() {
+ const searchRendered = () => {
Timing.end(CONST.TIMING.SEARCH_RENDER);
Performance.markEnd(CONST.TIMING.SEARCH_RENDER);
- }
-
- updateOptions() {
- const {recentReports, personalDetails, userToInvite} = OptionsListUtils.getSearchOptions(
- this.props.reports,
- this.props.personalDetails,
- this.state.searchValue.trim(),
- this.props.betas,
- );
- this.setState({
- userToInvite,
- recentReports,
- personalDetails,
- });
- }
+ };
+
+ const onChangeText = (value = '') => {
+ Report.searchInServer(searchValue);
+ setSearchValue(value);
+ };
/**
* Reset the search value and redirect to the selected report
*
* @param {Object} option
*/
- selectReport(option) {
+ const selectReport = (option) => {
if (!option) {
return;
}
-
if (option.reportID) {
- this.setState(
- {
- searchValue: '',
- },
- () => {
- Navigation.dismissModal(option.reportID);
- },
- );
+ Navigation.dismissModal(option.reportID);
} else {
Report.navigateToAndOpenReport([option.login]);
}
- }
-
- render() {
- const sections = this.getSections();
- const isOptionsDataReady = ReportUtils.isReportDataReady() && OptionsListUtils.isPersonalDetailsReady(this.props.personalDetails);
- const headerMessage = OptionsListUtils.getHeaderMessage(
- this.state.recentReports.length + this.state.personalDetails.length !== 0,
- Boolean(this.state.userToInvite),
- this.state.searchValue,
- );
-
- return (
-
- {({didScreenTransitionEnd, safeAreaPaddingBottomStyle}) => (
- <>
-
-
-
-
- >
- )}
-
- );
- }
+ };
+
+ const isOptionsDataReady = ReportUtils.isReportDataReady() && OptionsListUtils.isPersonalDetailsReady(personalDetails);
+ const headerMessage = OptionsListUtils.getHeaderMessage(
+ searchOptions.recentReports.length + searchOptions.personalDetails.length !== 0,
+ Boolean(searchOptions.userToInvite),
+ searchValue,
+ );
+
+ return (
+
+ {({didScreenTransitionEnd, safeAreaPaddingBottomStyle}) => (
+ <>
+
+
+
+
+ >
+ )}
+
+ );
}
SearchPage.propTypes = propTypes;
SearchPage.defaultProps = defaultProps;
SearchPage.displayName = 'SearchPage';
-
-export default compose(
- withLocalize,
- withWindowDimensions,
- withNetwork(),
- withOnyx({
- reports: {
- key: ONYXKEYS.COLLECTION.REPORT,
- },
- personalDetails: {
- key: ONYXKEYS.PERSONAL_DETAILS_LIST,
- },
- betas: {
- key: ONYXKEYS.BETAS,
- },
- isSearchingForReports: {
- key: ONYXKEYS.IS_SEARCHING_FOR_REPORTS,
- initWithStoredValues: false,
- },
- }),
- withThemeStyles,
-)(SearchPage);
+export default withOnyx({
+ reports: {
+ key: ONYXKEYS.COLLECTION.REPORT,
+ },
+ personalDetails: {
+ key: ONYXKEYS.PERSONAL_DETAILS_LIST,
+ },
+ betas: {
+ key: ONYXKEYS.BETAS,
+ },
+ isSearchingForReports: {
+ key: ONYXKEYS.IS_SEARCHING_FOR_REPORTS,
+ initWithStoredValues: false,
+ },
+})(SearchPage);