-
Notifications
You must be signed in to change notification settings - Fork 3k
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 MoneyRequestParticipantsSplitSelector.js to function component #20271
Changes from 4 commits
f8661e7
8ecd4fb
0b66de4
e10cd88
35b3f79
4c375f9
041d782
c068bfb
4dd5f0a
ffd6fad
49252c1
82be71e
a1e5ba1
05a9937
0afb3e6
b70b37e
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,4 +1,4 @@ | ||
import React, {Component} from 'react'; | ||
import React, {useEffect, useState} from 'react'; | ||
import {View} from 'react-native'; | ||
import PropTypes from 'prop-types'; | ||
import _ from 'underscore'; | ||
|
@@ -59,183 +59,161 @@ const defaultProps = { | |
safeAreaPaddingBottomStyle: {}, | ||
}; | ||
|
||
class MoneyRequestParticipantsSplitSelector extends Component { | ||
constructor(props) { | ||
super(props); | ||
|
||
this.toggleOption = this.toggleOption.bind(this); | ||
this.finalizeParticipants = this.finalizeParticipants.bind(this); | ||
this.updateOptionsWithSearchTerm = this.updateOptionsWithSearchTerm.bind(this); | ||
|
||
const {recentReports, personalDetails, userToInvite} = OptionsListUtils.getNewChatOptions( | ||
props.reports, | ||
props.personalDetails, | ||
props.betas, | ||
'', | ||
props.participants, | ||
CONST.EXPENSIFY_EMAILS, | ||
); | ||
|
||
this.state = { | ||
searchTerm: '', | ||
recentReports, | ||
personalDetails, | ||
userToInvite, | ||
}; | ||
} | ||
|
||
componentDidUpdate(prevProps) { | ||
if (_.isEqual(prevProps.reports, this.props.reports) && _.isEqual(prevProps.personalDetails, this.props.personalDetails)) { | ||
return; | ||
} | ||
this.updateOptionsWithSearchTerm(this.state.searchTerm); | ||
} | ||
function MoneyRequestParticipantsSplitSelector(props) { | ||
const [searchTerm, setSearchTerm] = useState(''); | ||
const [newChatOptions, setNewChatOptions] = useState({ | ||
recentReports: [], | ||
personalDetails: [], | ||
userToInvite: null, | ||
}); | ||
|
||
/** | ||
* Returns the sections needed for the OptionsSelector | ||
* | ||
* @param {Boolean} maxParticipantsReached | ||
* @returns {Array} | ||
*/ | ||
getSections(maxParticipantsReached) { | ||
const sections = []; | ||
const getSections = (maxParticipantsReached) => { | ||
const newSections = []; | ||
let indexOffset = 0; | ||
|
||
sections.push({ | ||
newSections.push({ | ||
title: undefined, | ||
data: this.props.participants, | ||
data: props.participants, | ||
shouldShow: true, | ||
indexOffset, | ||
}); | ||
indexOffset += this.props.participants.length; | ||
indexOffset += props.participants.length; | ||
|
||
if (maxParticipantsReached) { | ||
return sections; | ||
return newSections; | ||
} | ||
|
||
sections.push({ | ||
title: this.props.translate('common.recents'), | ||
data: this.state.recentReports, | ||
shouldShow: !_.isEmpty(this.state.recentReports), | ||
const { | ||
recentReports, | ||
personalDetails, | ||
userToInvite | ||
} = newChatOptions; | ||
|
||
newSections.push({ | ||
title: props.translate('common.recents'), | ||
data: recentReports, | ||
shouldShow: !_.isEmpty(recentReports), | ||
indexOffset, | ||
}); | ||
indexOffset += this.state.recentReports.length; | ||
indexOffset += recentReports.length; | ||
|
||
sections.push({ | ||
title: this.props.translate('common.contacts'), | ||
data: this.state.personalDetails, | ||
shouldShow: !_.isEmpty(this.state.personalDetails), | ||
newSections.push({ | ||
title: props.translate('common.contacts'), | ||
data: personalDetails, | ||
shouldShow: !_.isEmpty(personalDetails), | ||
indexOffset, | ||
}); | ||
indexOffset += this.state.personalDetails.length; | ||
indexOffset += personalDetails.length; | ||
|
||
if (this.state.userToInvite && !OptionsListUtils.isCurrentUser(this.state.userToInvite)) { | ||
sections.push({ | ||
if (userToInvite && !OptionsListUtils.isCurrentUser(userToInvite)) { | ||
newSections.push({ | ||
undefined, | ||
data: [this.state.userToInvite], | ||
data: [userToInvite], | ||
shouldShow: true, | ||
indexOffset, | ||
}); | ||
} | ||
|
||
return sections; | ||
return newSections; | ||
} | ||
|
||
updateOptionsWithSearchTerm(searchTerm = '') { | ||
/** | ||
* Removes a selected option from list if already selected. If not already selected add this option to the list. | ||
* @param {Object} option | ||
*/ | ||
const toggleOption = (option) => { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think we need to wrap this inside There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Wrapped inside useCallback. |
||
const isOptionInList = _.some(props.participants, (selectedOption) => selectedOption.login === option.login); | ||
|
||
let newSelectedOptions; | ||
|
||
if (isOptionInList) { | ||
newSelectedOptions = _.reject(props.participants, (selectedOption) => selectedOption.login === option.login); | ||
} else { | ||
newSelectedOptions = [...props.participants, option]; | ||
} | ||
|
||
const {recentReports, personalDetails, userToInvite} = OptionsListUtils.getNewChatOptions( | ||
this.props.reports, | ||
this.props.personalDetails, | ||
this.props.betas, | ||
searchTerm, | ||
this.props.participants, | ||
props.reports, | ||
props.personalDetails, | ||
props.betas, | ||
isOptionInList ? searchTerm : '', | ||
newSelectedOptions, | ||
CONST.EXPENSIFY_EMAILS, | ||
); | ||
this.setState({ | ||
searchTerm, | ||
userToInvite, | ||
|
||
setNewChatOptions({ | ||
recentReports, | ||
personalDetails, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Don't you need to set There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. No, we didn't. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. But in the original code, we're resetting the search term if option is in the list. Why were we doing that then? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yeah, you're right. |
||
userToInvite | ||
}); | ||
props.onAddParticipants(newSelectedOptions) | ||
} | ||
|
||
/** | ||
* Once a single or more users are selected, navigates to next step | ||
*/ | ||
finalizeParticipants() { | ||
this.props.onStepComplete(); | ||
const finalizeParticipants = () => { | ||
props.onStepComplete(); | ||
} | ||
|
||
/** | ||
* Removes a selected option from list if already selected. If not already selected add this option to the list. | ||
* @param {Object} option | ||
*/ | ||
toggleOption(option) { | ||
const isOptionInList = _.some(this.props.participants, (selectedOption) => selectedOption.login === option.login); | ||
|
||
let newSelectedOptions; | ||
|
||
if (isOptionInList) { | ||
newSelectedOptions = _.reject(this.props.participants, (selectedOption) => selectedOption.login === option.login); | ||
} else { | ||
newSelectedOptions = [...this.props.participants, option]; | ||
} | ||
const maxParticipantsReached = props.participants.length === CONST.REPORT.MAXIMUM_PARTICIPANTS; | ||
const headerMessage = OptionsListUtils.getHeaderMessage( | ||
newChatOptions.personalDetails.length + newChatOptions.recentReports.length !== 0, | ||
Boolean(newChatOptions.userToInvite), | ||
searchTerm, | ||
maxParticipantsReached, | ||
); | ||
const isOptionsDataReady = ReportUtils.isReportDataReady() && OptionsListUtils.isPersonalDetailsReady(props.personalDetails); | ||
|
||
this.props.onAddParticipants(newSelectedOptions); | ||
|
||
this.setState((prevState) => { | ||
const {recentReports, personalDetails, userToInvite} = OptionsListUtils.getNewChatOptions( | ||
this.props.reports, | ||
this.props.personalDetails, | ||
this.props.betas, | ||
isOptionInList ? prevState.searchTerm : '', | ||
newSelectedOptions, | ||
CONST.EXPENSIFY_EMAILS, | ||
); | ||
return { | ||
recentReports, | ||
personalDetails, | ||
userToInvite, | ||
searchTerm: isOptionInList ? prevState.searchTerm : '', | ||
}; | ||
}); | ||
} | ||
const sections = getSections(maxParticipantsReached); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why is this needed? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Hi, @allroundexperts |
||
|
||
render() { | ||
const maxParticipantsReached = this.props.participants.length === CONST.REPORT.MAXIMUM_PARTICIPANTS; | ||
const sections = this.getSections(maxParticipantsReached); | ||
const headerMessage = OptionsListUtils.getHeaderMessage( | ||
this.state.personalDetails.length + this.state.recentReports.length !== 0, | ||
Boolean(this.state.userToInvite), | ||
this.state.searchTerm, | ||
maxParticipantsReached, | ||
); | ||
const isOptionsDataReady = ReportUtils.isReportDataReady() && OptionsListUtils.isPersonalDetailsReady(this.props.personalDetails); | ||
|
||
return ( | ||
<View style={[styles.flex1, styles.w100, this.props.participants.length > 0 ? this.props.safeAreaPaddingBottomStyle : {}]}> | ||
<OptionsSelector | ||
canSelectMultipleOptions | ||
sections={sections} | ||
selectedOptions={this.props.participants} | ||
value={this.state.searchTerm} | ||
onSelectRow={this.toggleOption} | ||
onChangeText={this.updateOptionsWithSearchTerm} | ||
headerMessage={headerMessage} | ||
boldStyle | ||
shouldShowConfirmButton | ||
confirmButtonText={this.props.translate('common.next')} | ||
onConfirmSelection={this.finalizeParticipants} | ||
textInputLabel={this.props.translate('optionsSelector.nameEmailOrPhoneNumber')} | ||
safeAreaPaddingBottomStyle={this.props.safeAreaPaddingBottomStyle} | ||
shouldShowOptions={isOptionsDataReady} | ||
/> | ||
</View> | ||
useEffect(() => { | ||
const {recentReports, personalDetails, userToInvite} = OptionsListUtils.getNewChatOptions( | ||
props.reports, | ||
props.personalDetails, | ||
props.betas, | ||
searchTerm, | ||
props.participants, | ||
CONST.EXPENSIFY_EMAILS, | ||
); | ||
} | ||
} | ||
setNewChatOptions({ | ||
recentReports, | ||
personalDetails, | ||
userToInvite | ||
}); | ||
}, [props.betas, props.reports, props.participants, props.personalDetails, searchTerm]) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Hi @multijump, I think we should re-render if i.e. if the language is changed on device B, device A should re-render. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Hi, @rushatgabhane |
||
|
||
return ( | ||
<View style={[styles.flex1, styles.w100, props.participants.length > 0 ? props.safeAreaPaddingBottomStyle : {}]}> | ||
<OptionsSelector | ||
canSelectMultipleOptions | ||
sections={sections} | ||
selectedOptions={props.participants} | ||
value={searchTerm} | ||
onSelectRow={toggleOption} | ||
onChangeText={setSearchTerm} | ||
headerMessage={headerMessage} | ||
boldStyle | ||
shouldShowConfirmButton | ||
confirmButtonText={props.translate('common.next')} | ||
onConfirmSelection={finalizeParticipants} | ||
textInputLabel={props.translate('optionsSelector.nameEmailOrPhoneNumber')} | ||
safeAreaPaddingBottomStyle={props.safeAreaPaddingBottomStyle} | ||
shouldShowOptions={isOptionsDataReady} | ||
/> | ||
</View> | ||
); | ||
}; | ||
|
||
MoneyRequestParticipantsSplitSelector.propTypes = propTypes; | ||
MoneyRequestParticipantsSplitSelector.defaultProps = defaultProps; | ||
MoneyRequestParticipantsSplitSelector.displayName = 'MoneyRequestParticipantsSplitSelector'; | ||
|
||
export default compose( | ||
withLocalize, | ||
|
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.
We need to wrap this inside
useCallback
.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.
getSections
is not callback function so I think it is not good to use the useCallback.How about to use the useMemo?