diff --git a/src/pages/SearchPage.js b/src/pages/SearchPage.js index 6505b604b614..dd42ed80c3d4 100755 --- a/src/pages/SearchPage.js +++ b/src/pages/SearchPage.js @@ -1,5 +1,5 @@ import _ from 'underscore'; -import React, {Component} from 'react'; +import React, {useCallback, useEffect, useState, useMemo} from 'react'; import {View} from 'react-native'; import PropTypes from 'prop-types'; import {withOnyx} from 'react-native-onyx'; @@ -9,17 +9,15 @@ import * as ReportUtils from '../libs/ReportUtils'; import ONYXKEYS from '../ONYXKEYS'; import styles from '../styles/styles'; import Navigation from '../libs/Navigation/Navigation'; -import withWindowDimensions, {windowDimensionsPropTypes} from '../components/withWindowDimensions'; import * as Report from '../libs/actions/Report'; import HeaderWithBackButton from '../components/HeaderWithBackButton'; import ScreenWrapper from '../components/ScreenWrapper'; import Timing from '../libs/actions/Timing'; import CONST from '../CONST'; -import withLocalize, {withLocalizePropTypes} from '../components/withLocalize'; -import compose from '../libs/compose'; import personalDetailsPropType from './personalDetailsPropType'; import reportPropTypes from './reportPropTypes'; import Performance from '../libs/Performance'; +import useLocalize from '../hooks/useLocalize'; const propTypes = { /* Onyx Props */ @@ -32,11 +30,6 @@ const propTypes = { /** All reports shared with the user */ reports: PropTypes.objectOf(reportPropTypes), - - /** Window Dimensions Props */ - ...windowDimensionsPropTypes, - - ...withLocalizePropTypes, }; const defaultProps = { @@ -45,171 +38,157 @@ const defaultProps = { reports: {}, }; -class SearchPage extends Component { - constructor(props) { - super(props); +function SearchPage({betas, personalDetails, reports}) { + // Data for initialization (runs only on the first render) + const { + recentReports: initialRecentReports, + personalDetails: initialPersonalDetails, + userToInvite: initialUserToInvite, + // Ignoring the rule because in this case we need the data only initially + // eslint-disable-next-line react-hooks/exhaustive-deps + } = useMemo(() => OptionsListUtils.getSearchOptions(reports, personalDetails, '', betas), []); + + const [searchValue, setSearchValue] = useState(''); + const [searchOptions, setSearchOptions] = useState({ + recentReports: initialRecentReports, + personalDetails: initialPersonalDetails, + userToInvite: initialUserToInvite, + }); + + const {translate} = useLocalize(); + + 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.debouncedUpdateOptions = _.debounce(this.updateOptions.bind(this), 75); - - const {recentReports, personalDetails, userToInvite} = OptionsListUtils.getSearchOptions(props.reports, props.personalDetails, '', props.betas); - - this.state = { - searchValue: '', - recentReports, - personalDetails, - userToInvite, - }; - } - - componentDidUpdate(prevProps) { - if (_.isEqual(prevProps.reports, this.props.reports) && _.isEqual(prevProps.personalDetails, this.props.personalDetails)) { - return; - } - this.updateOptions(); - } - - onChangeText(searchValue = '') { - this.setState({searchValue}, this.debouncedUpdateOptions); - } + useEffect(() => { + debouncedUpdateOptions(); + }, [searchValue, debouncedUpdateOptions]); /** * 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 = '') => { + 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); - }, - ); + setSearchValue(''); + 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; - -export default compose( - withLocalize, - withWindowDimensions, - withOnyx({ - reports: { - key: ONYXKEYS.COLLECTION.REPORT, - }, - personalDetails: { - key: ONYXKEYS.PERSONAL_DETAILS_LIST, - }, - betas: { - key: ONYXKEYS.BETAS, - }, - }), -)(SearchPage); +SearchPage.displayName = 'SearchPage'; +export default withOnyx({ + reports: { + key: ONYXKEYS.COLLECTION.REPORT, + }, + personalDetails: { + key: ONYXKEYS.PERSONAL_DETAILS_LIST, + }, + betas: { + key: ONYXKEYS.BETAS, + }, +})(SearchPage);