From d4dbba3938f73c49edca403a3e4375b1fe58a231 Mon Sep 17 00:00:00 2001 From: "J. Ryan Stinnett" Date: Tue, 12 Mar 2019 16:17:21 +0000 Subject: [PATCH] Convert uncontrolled Field usages to controlled As part of adding validation to Field, the logic is simpler to follow if we can assume that all usages of Field use it as a controlled component, instead of supporting both controlled and uncontrolled. This converts the uncontrolled usages to controlled. --- src/components/views/auth/PasswordLogin.js | 9 ---- src/components/views/elements/Field.js | 6 +++ .../views/settings/ChangePassword.js | 54 ++++++++++++++++--- .../views/settings/EmailAddresses.js | 25 ++++++--- src/components/views/settings/PhoneNumbers.js | 45 ++++++++++++---- 5 files changed, 108 insertions(+), 31 deletions(-) diff --git a/src/components/views/auth/PasswordLogin.js b/src/components/views/auth/PasswordLogin.js index 4b095d405f..ed3afede2f 100644 --- a/src/components/views/auth/PasswordLogin.js +++ b/src/components/views/auth/PasswordLogin.js @@ -70,11 +70,6 @@ class PasswordLogin extends React.Component { this.isLoginEmpty = this.isLoginEmpty.bind(this); } - componentWillMount() { - this._passwordField = null; - this._loginField = null; - } - onForgotPasswordClick(ev) { ev.preventDefault(); ev.stopPropagation(); @@ -180,7 +175,6 @@ class PasswordLogin extends React.Component { return { this._loginField = e; }} name="username" // make it a little easier for browser's remember-password key="email_input" type="text" @@ -196,7 +190,6 @@ class PasswordLogin extends React.Component { return { this._loginField = e; }} name="username" // make it a little easier for browser's remember-password key="username_input" type="text" @@ -223,7 +216,6 @@ class PasswordLogin extends React.Component { return { this._loginField = e; }} name="phoneNumber" key="phone_input" type="text" @@ -344,7 +336,6 @@ class PasswordLogin extends React.Component { { this._passwordField = e; }} type="password" name="password" label={_t('Password')} diff --git a/src/components/views/elements/Field.js b/src/components/views/elements/Field.js index 36f6f02ff2..cd06610c62 100644 --- a/src/components/views/elements/Field.js +++ b/src/components/views/elements/Field.js @@ -32,6 +32,9 @@ export default class Field extends React.PureComponent { label: PropTypes.string, // The field's placeholder string. Defaults to the label. placeholder: PropTypes.string, + // The field's value. + // This is a controlled component, so the value is required. + value: PropTypes.string.isRequired, // Optional component to include inside the field before the input. prefix: PropTypes.node, // The callback called whenever the contents of the field @@ -50,6 +53,7 @@ export default class Field extends React.PureComponent { }; } + /* TODO: Remove me */ get value() { if (!this.refs.fieldInput) return null; return this.refs.fieldInput.value; @@ -87,6 +91,8 @@ export default class Field extends React.PureComponent { inputProps.placeholder = inputProps.placeholder || inputProps.label; inputProps.onChange = this.onChange; + + /* TODO: Remove me */ // make sure we use the current `value` for the field and not the original one if (inputProps.value === undefined) { inputProps.value = this.value || ""; diff --git a/src/components/views/settings/ChangePassword.js b/src/components/views/settings/ChangePassword.js index 69b80b03b3..ba708beaf4 100644 --- a/src/components/views/settings/ChangePassword.js +++ b/src/components/views/settings/ChangePassword.js @@ -32,6 +32,7 @@ import sessionStore from '../../../stores/SessionStore'; module.exports = React.createClass({ displayName: 'ChangePassword', + propTypes: { onFinished: PropTypes.func, onError: PropTypes.func, @@ -73,6 +74,9 @@ module.exports = React.createClass({ return { phase: this.Phases.Edit, cachedPassword: null, + oldPassword: "", + newPassword: "", + newPasswordConfirm: "", }; }, @@ -165,6 +169,9 @@ module.exports = React.createClass({ }).finally(() => { this.setState({ phase: this.Phases.Edit, + oldPassword: "", + newPassword: "", + newPasswordConfirm: "", }); }).done(); }, @@ -192,11 +199,29 @@ module.exports = React.createClass({ ); }, + onChangeOldPassword(ev) { + this.setState({ + oldPassword: ev.target.value, + }); + }, + + onChangeNewPassword(ev) { + this.setState({ + newPassword: ev.target.value, + }); + }, + + onChangeNewPasswordConfirm(ev) { + this.setState({ + newPasswordConfirm: ev.target.value, + }); + }, + onClickChange: function(ev) { ev.preventDefault(); - const oldPassword = this.state.cachedPassword || this.refs.old_input.value; - const newPassword = this.refs.new_input.value; - const confirmPassword = this.refs.confirm_input.value; + const oldPassword = this.state.cachedPassword || this.state.oldPassword; + const newPassword = this.state.newPassword; + const confirmPassword = this.state.newPasswordConfirm; const err = this.props.onCheckPassword( oldPassword, newPassword, confirmPassword, ); @@ -217,7 +242,12 @@ module.exports = React.createClass({ if (!this.state.cachedPassword) { currentPassword = (
- +
); } @@ -230,11 +260,21 @@ module.exports = React.createClass({
{ currentPassword }
- +
- +
{ _t('Change Password') } diff --git a/src/components/views/settings/EmailAddresses.js b/src/components/views/settings/EmailAddresses.js index 1ded71a5c7..b221f91901 100644 --- a/src/components/views/settings/EmailAddresses.js +++ b/src/components/views/settings/EmailAddresses.js @@ -119,6 +119,7 @@ export default class EmailAddresses extends React.Component { verifying: false, addTask: null, continueDisabled: false, + newEmailAddress: "", }; } @@ -134,14 +135,20 @@ export default class EmailAddresses extends React.Component { this.setState({emails: this.state.emails.filter((e) => e !== address)}); }; + _onChangeNewEmailAddress = (e) => { + this.setState({ + newEmailAddress: e.target.value, + }); + }; + _onAddClick = (e) => { e.stopPropagation(); e.preventDefault(); - if (!this.refs.newEmailAddress) return; + if (!this.state.newEmailAddress) return; const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog"); - const email = this.refs.newEmailAddress.value; + const email = this.state.newEmailAddress; // TODO: Inline field validation if (!Email.looksValid(email)) { @@ -173,14 +180,14 @@ export default class EmailAddresses extends React.Component { this.setState({continueDisabled: true}); this.state.addTask.checkEmailLinkClicked().then(() => { - const email = this.refs.newEmailAddress.value; + const email = this.state.newEmailAddress; this.setState({ emails: [...this.state.emails, {address: email, medium: "email"}], addTask: null, continueDisabled: false, verifying: false, + newEmailAddress: "", }); - this.refs.newEmailAddress.value = ""; }).catch((err) => { this.setState({continueDisabled: false}); if (err.errcode !== 'M_THREEPID_AUTH_FAILED') { @@ -221,8 +228,14 @@ export default class EmailAddresses extends React.Component { {existingEmailElements} - + {addButton} diff --git a/src/components/views/settings/PhoneNumbers.js b/src/components/views/settings/PhoneNumbers.js index b809240956..5d1d9b849f 100644 --- a/src/components/views/settings/PhoneNumbers.js +++ b/src/components/views/settings/PhoneNumbers.js @@ -117,6 +117,8 @@ export default class PhoneNumbers extends React.Component { addTask: null, continueDisabled: false, phoneCountry: "", + newPhoneNumber: "", + newPhoneNumberCode: "", }; } @@ -132,14 +134,26 @@ export default class PhoneNumbers extends React.Component { this.setState({msisdns: this.state.msisdns.filter((e) => e !== address)}); }; + _onChangeNewPhoneNumber = (e) => { + this.setState({ + newPhoneNumber: e.target.value, + }); + }; + + _onChangeNewPhoneNumberCode = (e) => { + this.setState({ + newPhoneNumberCode: e.target.value, + }); + }; + _onAddClick = (e) => { e.stopPropagation(); e.preventDefault(); - if (!this.refs.newPhoneNumber) return; + if (!this.state.newPhoneNumber) return; const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog"); - const phoneNumber = this.refs.newPhoneNumber.value; + const phoneNumber = this.state.newPhoneNumber; const phoneCountry = this.state.phoneCountry; const task = new AddThreepid(); @@ -162,7 +176,7 @@ export default class PhoneNumbers extends React.Component { e.preventDefault(); this.setState({continueDisabled: true}); - const token = this.refs.newPhoneNumberCode.value; + const token = this.state.newPhoneNumberCode; this.state.addTask.haveMsisdnToken(token).then(() => { this.setState({ msisdns: [...this.state.msisdns, {address: this.state.verifyMsisdn, medium: "msisdn"}], @@ -171,8 +185,9 @@ export default class PhoneNumbers extends React.Component { verifying: false, verifyMsisdn: "", verifyError: null, + newPhoneNumber: "", + newPhoneNumberCode: "", }); - this.refs.newPhoneNumber.value = ""; }).catch((err) => { this.setState({continueDisabled: false}); if (err.errcode !== 'M_THREEPID_AUTH_FAILED') { @@ -213,8 +228,14 @@ export default class PhoneNumbers extends React.Component { {this.state.verifyError}
- + {_t("Continue")} @@ -238,9 +259,15 @@ export default class PhoneNumbers extends React.Component {
- +
{addVerifySection}