Skip to content

Commit d68e40d

Browse files
committed
Providing workaround for rendering problems with forms when the route changes.
1 parent 657925d commit d68e40d

File tree

11 files changed

+124
-62
lines changed

11 files changed

+124
-62
lines changed

src/components/forms/EditUserProfileForm/EditUserProfileForm.js

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import React from 'react';
22
import PropTypes from 'prop-types';
33
import { FormattedMessage } from 'react-intl';
44
import { reduxForm, Field, change } from 'redux-form';
5+
import { lruMemoize } from 'reselect';
56
import isEmail from 'validator/lib/isEmail.js';
67

78
import FormBox from '../../widgets/FormBox';
@@ -12,6 +13,16 @@ import { SaveIcon } from '../../icons';
1213
import { validateRegistrationData } from '../../../redux/modules/users.js';
1314
import { TextField, PasswordField, PasswordStrength, CheckboxField } from '../Fields';
1415

16+
export const prepareInitialValues = lruMemoize(user => ({
17+
firstName: user.name.firstName,
18+
lastName: user.name.lastName,
19+
titlesBeforeName: user.name.titlesBeforeName,
20+
titlesAfterName: user.name.titlesAfterName,
21+
email: user.privateData.email,
22+
passwordStrength: null,
23+
gravatarUrlEnabled: user.avatarUrl !== null,
24+
}));
25+
1526
const EditUserProfileForm = ({
1627
submitting,
1728
handleSubmit,
Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,3 @@
1-
import EditUserProfileForm from './EditUserProfileForm.js';
1+
import EditUserProfileForm, { prepareInitialValues } from './EditUserProfileForm.js';
2+
export { prepareInitialValues };
23
export default EditUserProfileForm;

src/components/forms/EditUserRoleForm/EditUserRoleForm.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import { FormattedMessage, injectIntl } from 'react-intl';
44
import { reduxForm } from 'redux-form';
55
import { Table } from 'react-bootstrap';
66
import classnames from 'classnames';
7+
import { lruMemoize } from 'reselect';
78

89
import { knownRoles, roleLabels, roleDescriptions, UserRoleIcon } from '../../helpers/usersRoles.js';
910
import Callout from '../../widgets/Callout';
@@ -12,6 +13,8 @@ import { SaveIcon } from '../../icons';
1213
import SubmitButton from '../SubmitButton';
1314
import StandaloneRadioField from '../Fields/StandaloneRadioField.js';
1415

16+
export const prepareInitialValues = lruMemoize(user => ({ role: user.privateData.role }));
17+
1518
const EditUserRoleForm = ({
1619
currentRole = null,
1720
change,
Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,3 @@
1-
import EditUserRoleForm from './EditUserRoleForm.js';
1+
import EditUserRoleForm, { prepareInitialValues } from './EditUserRoleForm.js';
2+
export { prepareInitialValues };
23
export default EditUserRoleForm;

src/components/layout/PageContent/PageContent.js

Lines changed: 19 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -21,24 +21,26 @@ const getMessage = (item, formatMessage) =>
2121
* The component passes the title and description to the Helmet library
2222
* which reflects these into the <head> section of the HTML document.
2323
*/
24-
const PageContent = ({ intl: { formatMessage }, title = '', windowTitle = null, icon = null, children }) => (
25-
<main className="app-main bg-body-tertiary pb-1">
26-
<Helmet title={getMessage(windowTitle || title, formatMessage)} />
27-
{(title || icon) && (
28-
<div className="app-content-header">
29-
<h1 className="m-0 px-3 text-dark h3">
30-
{icon && (
31-
<span className="me-3 text-body-secondary">{typeof icon === 'string' ? <Icon icon={icon} /> : icon}</span>
32-
)}
33-
{title}
34-
</h1>
24+
const PageContent = ({ intl: { formatMessage }, title = '', windowTitle = null, icon = null, children }) => {
25+
return (
26+
<main className="app-main bg-body-tertiary pb-1">
27+
<Helmet title={getMessage(windowTitle || title, formatMessage)} />
28+
{(title || icon) && (
29+
<div className="app-content-header">
30+
<h1 className="m-0 px-3 text-dark h3">
31+
{icon && (
32+
<span className="me-3 text-body-secondary">{typeof icon === 'string' ? <Icon icon={icon} /> : icon}</span>
33+
)}
34+
{title}
35+
</h1>
36+
</div>
37+
)}
38+
<div className="app-content">
39+
<Container fluid>{children}</Container>
3540
</div>
36-
)}
37-
<div className="app-content">
38-
<Container fluid>{children}</Container>
39-
</div>
40-
</main>
41-
);
41+
</main>
42+
);
43+
};
4244

4345
PageContent.propTypes = {
4446
title: PropTypes.oneOfType([PropTypes.string, PropTypes.element]),

src/components/widgets/FormBox/FormBox.js

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,16 +2,19 @@ import React from 'react';
22
import PropTypes from 'prop-types';
33
import { Form } from 'react-bootstrap';
44
import Box from '../Box';
5+
import OnlyMounted from '../OnlyMounted';
56

67
/**
78
* A wrapper of Box adjusted for holding forms inside of the body.
89
*/
910
const FormBox = ({ id = null, onSubmit, succeeded = false, dirty = false, children, ...props }) => (
10-
<Form method="POST" onSubmit={onSubmit}>
11-
<Box id={id} type={succeeded ? 'success' : dirty ? 'warning' : 'secondary'} {...props} unlimitedHeight>
12-
<div>{children}</div>
13-
</Box>
14-
</Form>
11+
<OnlyMounted>
12+
<Form method="POST" onSubmit={onSubmit}>
13+
<Box id={id} type={succeeded ? 'success' : dirty ? 'warning' : 'secondary'} {...props} unlimitedHeight>
14+
<div>{children}</div>
15+
</Box>
16+
</Form>
17+
</OnlyMounted>
1518
);
1619

1720
FormBox.propTypes = {
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
import React, { Component } from 'react';
2+
import PropTypes from 'prop-types';
3+
4+
class OnlyMounted extends Component {
5+
state = { mounted: false };
6+
7+
componentDidMount() {
8+
this.setState({ mounted: true });
9+
}
10+
11+
componentWillUnmount() {
12+
this.setState({ mounted: false });
13+
}
14+
15+
render() {
16+
const { children, fallback = null } = this.props;
17+
return this.state.mounted
18+
? children
19+
: fallback || <div className="bg-secondary-subtle p-5 mb-1 w-100 rounded opacity-50" />;
20+
}
21+
}
22+
23+
OnlyMounted.propTypes = {
24+
fallback: PropTypes.any,
25+
children: PropTypes.any,
26+
};
27+
28+
export default OnlyMounted;
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export { default } from './OnlyMounted.js';

src/containers/App/App.js

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,8 +37,6 @@ const reloadIsRequired = (...statuses) =>
3737
statuses.includes(resourceStatus.FAILED) && !statuses.includes(resourceStatus.PENDING);
3838

3939
class App extends Component {
40-
static ignoreNextLocationChangeFlag = false;
41-
4240
static loadAsync =
4341
customLoadGroups =>
4442
(params, dispatch, { userId }) =>
@@ -154,6 +152,7 @@ class App extends Component {
154152

155153
render() {
156154
const { userId, instanceId } = this.props;
155+
157156
return userId && !instanceId ? (
158157
<div
159158
style={{

src/pages/EditUser/EditUser.js

Lines changed: 47 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -13,15 +13,19 @@ import Button, { TheButtonGroup } from '../../components/widgets/TheButton';
1313
import { LocalIcon, TransferIcon, EditUserIcon } from '../../components/icons';
1414
import { isStudentRole } from '../../components/helpers/usersRoles.js';
1515
import AllowUserButtonContainer from '../../containers/AllowUserButtonContainer';
16-
import EditUserProfileForm from '../../components/forms/EditUserProfileForm';
16+
import EditUserProfileForm, {
17+
prepareInitialValues as prepareUserProfileValues,
18+
} from '../../components/forms/EditUserProfileForm';
1719
import EditUserSettingsForm from '../../components/forms/EditUserSettingsForm';
1820
import EditUserUIDataForm, {
1921
EDITOR_FONT_SIZE_MIN,
2022
EDITOR_FONT_SIZE_MAX,
2123
EDITOR_FONT_SIZE_DEFAULT,
2224
} from '../../components/forms/EditUserUIDataForm';
2325
import GenerateTokenForm, { initialValues } from '../../components/forms/GenerateTokenForm';
24-
import EditUserRoleForm from '../../components/forms/EditUserRoleForm';
26+
import EditUserRoleForm, {
27+
prepareInitialValues as prepareUserRoleValues,
28+
} from '../../components/forms/EditUserRoleForm';
2529
import CalendarTokens from '../../components/Users/CalendarTokens';
2630
import Box from '../../components/widgets/Box';
2731
import ResourceRenderer from '../../components/helpers/ResourceRenderer';
@@ -44,6 +48,8 @@ import {
4448
setUserCalendarExpired,
4549
} from '../../redux/modules/userCalendars.js';
4650
import { getUserCalendars } from '../../redux/selectors/userCalendars.js';
51+
import { EMPTY_OBJ } from '../../helpers/common.js';
52+
import OnlyMounted from '../../components/widgets/OnlyMounted/OnlyMounted.js';
4753

4854
const prepareNumber = (number, min, max, defaultValue) => {
4955
number = Number(number);
@@ -78,10 +84,16 @@ const prepareUserUIDataInitialValues = lruMemoize(
7884
);
7985

8086
class EditUser extends Component {
87+
constructor(props) {
88+
super(props);
89+
this.user = null;
90+
}
91+
8192
static loadAsync = ({ userId }, dispatch) =>
8293
Promise.all([dispatch(fetchUserIfNeeded(userId)), dispatch(fetchUserCalendarsIfNeeded(userId))]);
8394

8495
componentDidMount() {
96+
this.setState({ mounted: true });
8597
this.props.loadAsync();
8698
}
8799

@@ -163,48 +175,47 @@ class EditUser extends Component {
163175

164176
<Row>
165177
<Col lg={6}>
166-
<EditUserProfileForm
167-
onSubmit={formData => this.updateProfile(formData, isSuperAdmin || !data.privateData.isExternal)}
168-
initialValues={{
169-
firstName: data.name.firstName,
170-
lastName: data.name.lastName,
171-
titlesBeforeName: data.name.titlesBeforeName,
172-
titlesAfterName: data.name.titlesAfterName,
173-
email: data.privateData.email,
174-
passwordStrength: null,
175-
gravatarUrlEnabled: data.avatarUrl !== null,
176-
}}
177-
allowChangePassword={data.privateData.isLocal}
178-
emptyLocalPassword={data.privateData.emptyLocalPassword}
179-
canForceChangePassword={isSuperAdmin && data.id !== loggedUserId}
180-
disabledNameChange={data.privateData.isExternal && !isSuperAdmin}
181-
/>
178+
<OnlyMounted>
179+
<EditUserProfileForm
180+
onSubmit={formData => this.updateProfile(formData, isSuperAdmin || !data.privateData.isExternal)}
181+
initialValues={prepareUserProfileValues(data)}
182+
allowChangePassword={data.privateData.isLocal}
183+
emptyLocalPassword={data.privateData.emptyLocalPassword}
184+
canForceChangePassword={isSuperAdmin && data.id !== loggedUserId}
185+
disabledNameChange={data.privateData.isExternal && !isSuperAdmin}
186+
/>
187+
</OnlyMounted>
182188
</Col>
183189

184190
{data.id === loggedUserId && (
185191
<Col lg={6}>
186192
{data.privateData.settings && (
187-
<EditUserSettingsForm
188-
onSubmit={updateSettings}
189-
user={data}
190-
initialValues={data.privateData.settings}
191-
/>
193+
<OnlyMounted>
194+
<EditUserSettingsForm
195+
onSubmit={updateSettings}
196+
user={data}
197+
initialValues={data.privateData.settings}
198+
/>
199+
</OnlyMounted>
192200
)}
193-
194-
<EditUserUIDataForm
195-
onSubmit={updateUIData}
196-
initialValues={prepareUserUIDataInitialValues(data.privateData.uiData || {})}
197-
/>
201+
<OnlyMounted>
202+
<EditUserUIDataForm
203+
onSubmit={updateUIData}
204+
initialValues={prepareUserUIDataInitialValues(data.privateData.uiData || EMPTY_OBJ)}
205+
/>
206+
</OnlyMounted>
198207
</Col>
199208
)}
200209

201210
{isSuperAdmin && data.id !== loggedUserId && data.privateData && (
202211
<Col lg={6}>
203-
<EditUserRoleForm
204-
currentRole={data.privateData.role}
205-
initialValues={{ role: data.privateData.role }}
206-
onSubmit={this.setRole}
207-
/>
212+
<OnlyMounted>
213+
<EditUserRoleForm
214+
currentRole={data.privateData.role}
215+
initialValues={prepareUserRoleValues(data)}
216+
onSubmit={this.setRole}
217+
/>
218+
</OnlyMounted>
208219
</Col>
209220
)}
210221
</Row>
@@ -232,7 +243,9 @@ class EditUser extends Component {
232243

233244
<Row>
234245
<Col lg={12}>
235-
<GenerateTokenForm onSubmit={generateToken} initialValues={initialValues} lastToken={lastToken} />
246+
<OnlyMounted>
247+
<GenerateTokenForm onSubmit={generateToken} initialValues={initialValues} lastToken={lastToken} />
248+
</OnlyMounted>
236249
</Col>
237250
</Row>
238251
</>

0 commit comments

Comments
 (0)