diff --git a/web/modals/account/forgot-password-modal.react.js b/web/modals/account/forgot-password-modal.react.js index 2fbca6786..eb1d5133e 100644 --- a/web/modals/account/forgot-password-modal.react.js +++ b/web/modals/account/forgot-password-modal.react.js @@ -1,177 +1,190 @@ // @flow import invariant from 'invariant'; import PropTypes from 'prop-types'; import * as React from 'react'; import { forgotPasswordActionTypes, forgotPassword, } from 'lib/actions/user-actions'; import { createLoadingStatusSelector } from 'lib/selectors/loading-selectors'; import { oldValidUsernameRegex, validEmailRegex, } from 'lib/shared/account-utils'; -import type { DispatchActionPromise } from 'lib/utils/action-utils'; -import { connect } from 'lib/utils/redux-utils'; +import { + type DispatchActionPromise, + useDispatchActionPromise, + useServerCall, +} from 'lib/utils/action-utils'; -import type { AppState } from '../../redux/redux-setup'; +import { useSelector } from '../../redux/redux-utils'; import css from '../../style.css'; import Modal from '../modal.react'; import PasswordResetEmailModal from './password-reset-email-modal.react'; -type Props = {| +type BaseProps = {| +setModal: (modal: ?React.Node) => void, - // Redux state +|}; +type Props = {| + ...BaseProps, +inputDisabled: boolean, - // Redux dispatch functions +dispatchActionPromise: DispatchActionPromise, - // async functions that hit server APIs +forgotPassword: (usernameOrEmail: string) => Promise, |}; type State = {| +usernameOrEmail: string, +errorMessage: string, |}; class ForgotPasswordModal extends React.PureComponent { props: Props; state: State; usernameOrEmailInput: ?HTMLInputElement; constructor(props: Props) { super(props); this.state = { usernameOrEmail: '', errorMessage: '', }; } componentDidMount() { invariant(this.usernameOrEmailInput, 'usernameOrEmail ref unset'); this.usernameOrEmailInput.focus(); } render() { return (
Username
{this.state.errorMessage}
); } usernameOrEmailInputRef = (usernameOrEmailInput: ?HTMLInputElement) => { this.usernameOrEmailInput = usernameOrEmailInput; }; onChangeUsernameOrEmail = (event: SyntheticEvent) => { const target = event.target; invariant(target instanceof HTMLInputElement, 'target not input'); this.setState({ usernameOrEmail: target.value }); }; onSubmit = (event: SyntheticEvent) => { event.preventDefault(); if ( this.state.usernameOrEmail.search(oldValidUsernameRegex) === -1 && this.state.usernameOrEmail.search(validEmailRegex) === -1 ) { this.setState( { usernameOrEmail: '', errorMessage: 'alphanumeric usernames or emails only', }, () => { invariant( this.usernameOrEmailInput, 'usernameOrEmailInput ref unset', ); this.usernameOrEmailInput.focus(); }, ); return; } this.props.dispatchActionPromise( forgotPasswordActionTypes, this.forgotPasswordAction(), ); }; async forgotPasswordAction() { try { await this.props.forgotPassword(this.state.usernameOrEmail); this.props.setModal( , ); } catch (e) { this.setState( { usernameOrEmail: '', errorMessage: e.message === 'invalid_user' ? "user doesn't exist" : 'unknown error', }, () => { invariant( this.usernameOrEmailInput, 'usernameOrEmailInput ref unset', ); this.usernameOrEmailInput.focus(); }, ); throw e; } } clearModal = () => { this.props.setModal(null); }; } ForgotPasswordModal.propTypes = { setModal: PropTypes.func.isRequired, inputDisabled: PropTypes.bool.isRequired, dispatchActionPromise: PropTypes.func.isRequired, forgotPassword: PropTypes.func.isRequired, }; const loadingStatusSelector = createLoadingStatusSelector( forgotPasswordActionTypes, ); -export default connect( - (state: AppState) => ({ - inputDisabled: loadingStatusSelector(state) === 'loading', - }), - { forgotPassword }, -)(ForgotPasswordModal); +export default React.memo(function ConnectedForgotPasswordModal( + props: BaseProps, +) { + const inputDisabled = useSelector(loadingStatusSelector) === 'loading'; + const callForgotPassword = useServerCall(forgotPassword); + const dispatchActionPromise = useDispatchActionPromise(); + + return ( + + ); +});