Skip to content

Commit e07a6c8

Browse files
Refactor Password Reset: Convert Class-Based Component to Functional Component (#7697)
1 parent a30764e commit e07a6c8

File tree

2 files changed

+109
-191
lines changed

2 files changed

+109
-191
lines changed

packages/volto/news/7697.internal

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Refactored the `PasswordReset` widget by converting it from a class-based component to a modern functional component using React hooks. @Manik-Khajuria-5

packages/volto/src/components/theme/PasswordReset/PasswordReset.jsx

Lines changed: 108 additions & 191 deletions
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,12 @@
33
* @module components/theme/PasswordReset/PasswordReset
44
*/
55

6-
import React, { Component } from 'react';
7-
import PropTypes from 'prop-types';
8-
import { connect } from 'react-redux';
9-
import { compose } from 'redux';
10-
import { Link, withRouter } from 'react-router-dom';
6+
import { useState, useEffect } from 'react';
7+
import { useSelector, useDispatch } from 'react-redux';
8+
import { Link, useHistory, useParams } from 'react-router-dom';
119
import Helmet from '@plone/volto/helpers/Helmet/Helmet';
1210
import { Container } from 'semantic-ui-react';
13-
import { FormattedMessage, defineMessages, injectIntl } from 'react-intl';
11+
import { FormattedMessage, defineMessages, useIntl } from 'react-intl';
1412

1513
import { Form } from '@plone/volto/components/manage/Form';
1614
import { setInitialPassword } from '@plone/volto/actions/users/users';
@@ -90,220 +88,139 @@ const messages = defineMessages({
9088
});
9189

9290
/**
93-
* PasswordReset class.
94-
* @class PasswordReset
95-
* @extends Component
91+
* @function PasswordReset
92+
* @returns {JSX.Element}
9693
*/
97-
class PasswordReset extends Component {
98-
/**
99-
* Property types.
100-
* @property {Object} propTypes Property types.
101-
* @static
102-
*/
103-
static propTypes = {
104-
loading: PropTypes.bool.isRequired,
105-
loaded: PropTypes.bool.isRequired,
106-
error: PropTypes.string,
107-
token: PropTypes.string.isRequired,
108-
setInitialPassword: PropTypes.func.isRequired,
109-
};
94+
function PasswordReset() {
95+
const dispatch = useDispatch();
96+
const history = useHistory();
97+
const { token } = useParams();
11098

111-
/**
112-
* Default properties.
113-
* @property {Object} defaultProps Default properties.
114-
* @static
115-
*/
116-
static defaultProps = {
117-
error: null,
118-
};
99+
const loading = useSelector((state) => state.users.initial.loading);
100+
const loaded = useSelector((state) => state.users.initial.loaded);
101+
const error = useSelector((state) => state.users.initial.error);
119102

120-
/**
121-
* Constructor
122-
* @method constructor
123-
* @param {Object} props Component properties
124-
* @constructs Controlpanel
125-
*/
126-
constructor(props) {
127-
super(props);
128-
this.onCancel = this.onCancel.bind(this);
129-
this.onSubmit = this.onSubmit.bind(this);
130-
this.state = {
131-
error: null,
132-
isSuccessful: false,
133-
};
103+
const [localError, setLocalError] = useState(null);
104+
const [isSuccessful, setIsSuccessful] = useState(false);
105+
const intl = useIntl();
134106

135-
this.identifierField = config.settings.useEmailAsLogin
136-
? 'email'
137-
: 'username';
107+
const identifierField = config.settings.useEmailAsLogin
108+
? 'email'
109+
: 'username';
138110

139-
this.identifierTitle =
140-
this.identifierField === 'email'
141-
? this.props.intl.formatMessage(messages.emailTitle)
142-
: this.props.intl.formatMessage(messages.usernameTitle);
111+
const identifierTitle =
112+
identifierField === 'email'
113+
? intl.formatMessage(messages.emailTitle)
114+
: intl.formatMessage(messages.usernameTitle);
143115

144-
this.identifierDescription =
145-
this.identifierField === 'email'
146-
? this.props.intl.formatMessage(messages.emailDescription)
147-
: this.props.intl.formatMessage(messages.usernameDescription);
148-
}
116+
const identifierDescription =
117+
identifierField === 'email'
118+
? intl.formatMessage(messages.emailDescription)
119+
: intl.formatMessage(messages.usernameDescription);
149120

150-
/**
151-
* Component will receive props
152-
* @method componentWillReceiveProps
153-
* @param {Object} nextProps Next properties
154-
* @returns {undefined}
155-
*/
156-
UNSAFE_componentWillReceiveProps(nextProps) {
157-
if (this.props.loading && nextProps.loaded) {
158-
this.setState({ isSuccessful: true });
121+
useEffect(() => {
122+
if (!loading && loaded) {
123+
setIsSuccessful(true);
159124
}
160-
}
125+
}, [loading, loaded]);
161126

162127
/**
163128
* Submit handler
164129
* @method onSubmit
165130
* @param {object} data Form data.
166-
* @param {object} event Form data.
167131
* @returns {undefined}
168132
*/
169-
onSubmit(data) {
133+
const onSubmit = (data) => {
170134
if (data.password === data.passwordRepeat) {
171-
this.props.setInitialPassword(
172-
data[this.identifierField],
173-
this.props.token,
174-
data.password,
175-
);
176-
this.setState({
177-
error: null,
178-
});
135+
dispatch(setInitialPassword(data[identifierField], token, data.password));
136+
setLocalError(null);
179137
} else {
180-
this.setState({
181-
error: {
182-
message: this.props.intl.formatMessage(messages.passwordsDoNotMatch),
183-
},
138+
setLocalError({
139+
message: intl.formatMessage(messages.passwordsDoNotMatch),
184140
});
185141
}
186-
}
142+
};
187143

188144
/**
189145
* Cancel handler
190146
* @method onCancel
191147
* @returns {undefined}
192148
*/
193-
onCancel() {
194-
this.props.history.goBack();
149+
const onCancel = () => {
150+
history.goBack();
151+
};
152+
153+
if (isSuccessful) {
154+
return (
155+
<Container>
156+
<h1 className="documentFirstHeading">
157+
<FormattedMessage {...messages.successRedirectToLoginTitle} />
158+
</h1>
159+
<p className="description">
160+
<FormattedMessage
161+
{...messages.successRedirectToLoginBody}
162+
values={{
163+
link: (
164+
<Link to="/login">{intl.formatMessage({ id: 'Log In' })}</Link>
165+
),
166+
}}
167+
/>
168+
</p>
169+
</Container>
170+
);
195171
}
196172

197-
/**
198-
* Render method.
199-
* @method render
200-
* @returns {string} Markup for the component.
201-
*/
202-
render() {
203-
if (this.state.isSuccessful) {
204-
return (
173+
if (token) {
174+
const errmsg = error ? error.response?.body?.error || error : null;
175+
return (
176+
<div id="page-password-reset">
177+
<Helmet title={intl.formatMessage(messages.passwordReset)} />
205178
<Container>
206-
<h1 className="documentFirstHeading">
207-
<FormattedMessage
208-
id="Account activation completed"
209-
defaultMessage="Account activation completed"
210-
/>
211-
</h1>
212-
<p className="description">
213-
<FormattedMessage
214-
id="Your password has been set successfully. You may now {link} with your new password."
215-
defaultMessage="Your password has been set successfully. You may now {link} with your new password."
216-
values={{
217-
link: (
218-
<Link to="/login">
219-
{this.props.intl.formatMessage({ id: 'Log In' })}
220-
</Link>
221-
),
222-
}}
223-
/>
224-
</p>
225-
</Container>
226-
);
227-
}
228-
if (this.props.token) {
229-
const errmsg = this.props.error
230-
? this.props.error.response.body.error
231-
: null;
232-
return (
233-
<div id="page-password-reset">
234-
<Helmet
235-
title={this.props.intl.formatMessage(messages.passwordReset)}
236-
/>
237-
<Container>
238-
<Form
239-
title={this.props.intl.formatMessage(messages.title)}
240-
description={this.props.intl.formatMessage(messages.description)}
241-
onSubmit={this.onSubmit}
242-
onCancel={this.onCancel}
243-
error={this.state.error || errmsg}
244-
schema={{
245-
fieldsets: [
246-
{
247-
id: 'default',
248-
title: this.props.intl.formatMessage(messages.default),
249-
fields: [
250-
this.identifierField,
251-
'password',
252-
'passwordRepeat',
253-
],
254-
},
255-
],
256-
properties: {
257-
[this.identifierField]: {
258-
type: 'string',
259-
title: this.identifierTitle,
260-
description: this.identifierDescription,
261-
},
262-
password: {
263-
description: this.props.intl.formatMessage(
264-
messages.passwordDescription,
265-
),
266-
title: this.props.intl.formatMessage(
267-
messages.passwordTitle,
268-
),
269-
type: 'string',
270-
widget: 'password',
271-
},
272-
passwordRepeat: {
273-
description: this.props.intl.formatMessage(
274-
messages.passwordRepeatDescription,
275-
),
276-
title: this.props.intl.formatMessage(
277-
messages.passwordRepeatTitle,
278-
),
279-
type: 'string',
280-
widget: 'password',
281-
},
179+
<Form
180+
title={intl.formatMessage(messages.title)}
181+
description={intl.formatMessage(messages.description)}
182+
onSubmit={onSubmit}
183+
onCancel={onCancel}
184+
error={localError || errmsg}
185+
schema={{
186+
fieldsets: [
187+
{
188+
id: 'default',
189+
title: intl.formatMessage(messages.default),
190+
fields: [identifierField, 'password', 'passwordRepeat'],
282191
},
283-
submitLabel: this.props.intl.formatMessage(
284-
messages.setMyPassword,
285-
),
286-
required: [this.identifierField, 'password', 'passwordRepeat'],
287-
}}
288-
/>
289-
</Container>
290-
</div>
291-
);
292-
}
293-
return <div />;
192+
],
193+
properties: {
194+
[identifierField]: {
195+
type: 'string',
196+
title: identifierTitle,
197+
description: identifierDescription,
198+
},
199+
password: {
200+
description: intl.formatMessage(messages.passwordDescription),
201+
title: intl.formatMessage(messages.passwordTitle),
202+
type: 'string',
203+
widget: 'password',
204+
},
205+
passwordRepeat: {
206+
description: intl.formatMessage(
207+
messages.passwordRepeatDescription,
208+
),
209+
title: intl.formatMessage(messages.passwordRepeatTitle),
210+
type: 'string',
211+
widget: 'password',
212+
},
213+
},
214+
submitLabel: intl.formatMessage(messages.setMyPassword),
215+
required: [identifierField, 'password', 'passwordRepeat'],
216+
}}
217+
/>
218+
</Container>
219+
</div>
220+
);
294221
}
222+
223+
return <div />;
295224
}
296225

297-
export default compose(
298-
withRouter,
299-
injectIntl,
300-
connect(
301-
(state, props) => ({
302-
loading: state.users.initial.loading,
303-
loaded: state.users.initial.loaded,
304-
error: state.users.initial.error,
305-
token: props.match.params.token,
306-
}),
307-
{ setInitialPassword },
308-
),
309-
)(PasswordReset);
226+
export default PasswordReset;

0 commit comments

Comments
 (0)