Unverified Commit 1ed6b568 authored by Zhou (Link)  Fang's avatar Zhou (Link) Fang Committed by GitHub
Browse files

Improved the useEffect usage (#107)

* improved useEffect usage

* fixed small issues after solved the conflicts

* fixed some router issues by changing location.pathname to location.hash

* added a missing dependency

* temp save

* made changes to improve the usage of useEffect

* minor changes

* minor update on if statement in wgwraper component
parent 03db5521
......@@ -53,5 +53,9 @@
"react-datepicker": "^3.2.2",
"react-select": "^4.1.0",
"yup": "^0.32.8"
},
"prettier": {
"singleQuote": true,
"printWidth": 80
}
}
......@@ -343,13 +343,23 @@ export function matchWGFieldsToBackend(eachWorkingGroupData, formId) {
* @param formId - Form Id fetched from the server, sotored in membership context, used for calling APIs
* @param userId - User Id fetched from the server when sign in, sotored in membership context, used for calling APIs
*/
export async function executeSendDataByStep(step, formData, formId, userId) {
export async function executeSendDataByStep(
step,
formData,
formId,
userId,
setFieldValueObj
) {
switch (step) {
case 1:
callSendData(
formId,
END_POINT.organizations,
matchCompanyFieldsToBackend(formData.organization, formId)
matchCompanyFieldsToBackend(formData.organization, formId),
{
fieldName: setFieldValueObj.fieldName.organization,
method: setFieldValueObj.method,
}
);
callSendData(
formId,
......@@ -358,7 +368,11 @@ export async function executeSendDataByStep(step, formData, formId, userId) {
formData.representative.member,
CONTACT_TYPE.COMPANY,
formId
)
),
{
fieldName: setFieldValueObj.fieldName.member,
method: setFieldValueObj.method,
}
);
callSendData(
formId,
......@@ -367,7 +381,11 @@ export async function executeSendDataByStep(step, formData, formId, userId) {
formData.representative.marketing,
CONTACT_TYPE.MARKETING,
formId
)
),
{
fieldName: setFieldValueObj.fieldName.marketing,
method: setFieldValueObj.method,
}
);
callSendData(
formId,
......@@ -376,7 +394,11 @@ export async function executeSendDataByStep(step, formData, formId, userId) {
formData.representative.accounting,
CONTACT_TYPE.ACCOUNTING,
formId
)
),
{
fieldName: setFieldValueObj.fieldName.accounting,
method: setFieldValueObj.method,
}
);
callSendData(
formId,
......@@ -394,11 +416,13 @@ export async function executeSendDataByStep(step, formData, formId, userId) {
break;
case 3:
formData.workingGroups.forEach((item) => {
formData.workingGroups.forEach((item, index) => {
callSendData(
formId,
END_POINT.working_groups,
matchWGFieldsToBackend(item, formId)
matchWGFieldsToBackend(item, formId),
setFieldValueObj,
index
);
});
break;
......@@ -411,7 +435,8 @@ export async function executeSendDataByStep(step, formData, formId, userId) {
formData.signingAuthorityRepresentative,
CONTACT_TYPE.SIGNING,
formId
)
),
setFieldValueObj
);
return;
......@@ -428,7 +453,13 @@ export async function executeSendDataByStep(step, formData, formId, userId) {
* If empty, is creating a new entity, use POST method;
* If has value, is fetched from server, use PUT or DELETE;
*/
function callSendData(formId, endpoint = '', dataBody) {
function callSendData(
formId,
endpoint = '',
dataBody,
setFieldValueObj,
index
) {
const entityId = dataBody.id ? dataBody.id : '';
const method = dataBody.id ? FETCH_METHOD.PUT : FETCH_METHOD.POST;
......@@ -454,9 +485,71 @@ function callSendData(formId, endpoint = '', dataBody) {
method: method,
headers: FETCH_HEADER,
body: JSON.stringify(dataBody),
}).then((res) => {
console.log(res.status);
});
})
.then((res) => {
console.log(res.status);
return res.json();
})
.then((data) => {
if (setFieldValueObj && method === 'POST') {
// update the field id after a successful post
switch (setFieldValueObj.fieldName) {
case 'organization':
setFieldValueObj.method(
`${setFieldValueObj.fieldName}.id`,
data[0].id
);
setFieldValueObj.method(
'organization.address.id',
data[0]?.address.id
);
break;
case 'representative.member':
setFieldValueObj.method(
`${setFieldValueObj.fieldName}.id`,
data.id
);
break;
case 'representative.marketing':
setFieldValueObj.method(
`${setFieldValueObj.fieldName}.id`,
data.id
);
break;
case 'representative.accounting':
setFieldValueObj.method(
`${setFieldValueObj.fieldName}.id`,
data.id
);
break;
case 'workingGroups':
setFieldValueObj.method(`workingGroups[${index}].id`, data[0].id);
setFieldValueObj.method(
`workingGroups[${index}].workingGroupRepresentative.id`,
data[0].contact.id
);
break;
case 'signingAuthorityRepresentative':
setFieldValueObj.method.signingAuthority(
`${setFieldValueObj.fieldName}.id`,
data.id
);
setFieldValueObj.method.companyInfo(
`${setFieldValueObj.fieldName}.id`,
data.id
);
break;
default:
break;
}
}
});
}
}
......@@ -519,9 +612,7 @@ export function deleteData(formId, endpoint, entityId, callback, index) {
* - Send the API calls to organizations and contacts
* **/
export async function handleNewForm(setCurrentFormId, defaultBehaviour) {
if (getCurrentMode() === MODE_REACT_ONLY) {
defaultBehaviour();
}
defaultBehaviour();
if (getCurrentMode() === MODE_REACT_API) {
var dataBody = {
......@@ -538,7 +629,6 @@ export async function handleNewForm(setCurrentFormId, defaultBehaviour) {
.then((data) => {
console.log('Start with a new form:', data);
setCurrentFormId(data[0]?.id);
defaultBehaviour();
})
.catch((err) => console.log(err));
}
......
......@@ -43,13 +43,14 @@ const useStyles = makeStyles(() => ({
},
}));
let hasOrgData = false;
let hasMembershipLevelData = false;
const CompanyInformation = ({ formik, isStartNewForm }) => {
const { currentFormId, furthestPage } = useContext(MembershipContext); // current chosen form id
const { currentFormId } = useContext(MembershipContext); // current chosen form id
const [loading, setLoading] = useState(true);
const { setFieldValue } = formik;
// Fetch data only once and prefill data,
// as long as currentFormId and setFieldValue
// Function does not change, will not cause re-render again
useEffect(() => {
const detectModeAndFetch = () => {
// Once we have API set up ready, we don't need the
......@@ -73,7 +74,7 @@ const CompanyInformation = ({ formik, isStartNewForm }) => {
if (getCurrentMode() === MODE_REACT_API) {
url_prefix_local = API_PREFIX_FORM;
}
// If the current form exsits, and it is not creating a new form
// If the current form id exsits
if (currentFormId) {
// Using promise pool, because in first step,
// need to get company data, and contacts data
......@@ -107,8 +108,10 @@ const CompanyInformation = ({ formik, isStartNewForm }) => {
// organization field with the mapped data,
// if nested, it will automatically map the
// properties and values
formik.setFieldValue('organization', tempOrg);
setFieldValue('organization', tempOrg);
hasOrgData = true;
}
if (contacts.length) {
// Call the the function to map the retrived contacts
// (company representative, marketing rep, accounting rep)
......@@ -117,15 +120,16 @@ const CompanyInformation = ({ formik, isStartNewForm }) => {
// Prefill Data --> Call the setFieldValue of Formik,
// to set representative field with the mapped data,
// if nested, it will automatically map the properties and values
formik.setFieldValue(
setFieldValue(
'representative',
tempContacts.organizationContacts
);
formik.setFieldValue(
setFieldValue(
'signingAuthorityRepresentative',
tempContacts.signingAuthorityRepresentative
);
hasOrgData = true;
}
setLoading(false);
});
......@@ -162,17 +166,12 @@ const CompanyInformation = ({ formik, isStartNewForm }) => {
data[0]?.membership_level,
MEMBERSHIP_LEVELS
);
formik.setFieldValue(
'membershipLevel',
tempMembershipLevel.value
);
formik.setFieldValue(
'membershipLevel-label',
tempMembershipLevel
);
setFieldValue('membershipLevel', tempMembershipLevel.value);
setFieldValue('membershipLevel-label', tempMembershipLevel);
const tempPurchasingAndVAT = mapPurchasingAndVAT(data[0]);
formik.setFieldValue('purchasingAndVAT', tempPurchasingAndVAT);
setFieldValue('purchasingAndVAT', tempPurchasingAndVAT);
hasMembershipLevelData = true;
}
setLoading(false);
});
......@@ -182,30 +181,16 @@ const CompanyInformation = ({ formik, isStartNewForm }) => {
};
if (isStartNewForm) {
if (furthestPage.index > 1 && !formik.values.organization?.id) {
// This means user already submitted/finished this page, and comes back from a further page/step
// so, we need to GET the info user submitted and if user changes anything,
// we will use the organization_id from the GET to do the PUT to update the info.
detectModeAndFetch();
setLoading(false);
} else {
// This means this is the 1st time the user see this page,
// or the user already got the organizations.id
// no need to do any API call
setLoading(false);
}
} else if (!formik.values.organization?.id) {
// continue with an existing one, if there is no id saved locally
setLoading(false);
} else {
// continue with an existing one, if there is no data saved locally
// then it means this is the 1st time the user see this page
// need to GET the data
detectModeAndFetch();
detectModeAndFetchMembershipLevel();
} else {
// user already has the data, no need to do any API call
setLoading(false);
if (!hasOrgData) detectModeAndFetch();
if (!hasMembershipLevelData) detectModeAndFetchMembershipLevel();
if (hasOrgData && hasMembershipLevelData) setLoading(false);
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
}, [isStartNewForm, setFieldValue, currentFormId]);
// If it is in loading status,
// only return a loading spinning
......
import { useEffect } from 'react';
import Input from '../../UIComponents/Inputs/Input';
import { formField } from '../../UIComponents/FormComponents/formFieldModel';
import { Checkbox, FormControlLabel } from '@material-ui/core';
......@@ -37,31 +36,45 @@ const Contacts = ({ formik }) => {
* @param disableInput - if marketing / accounting is the same as company Rep., mark the input disabled and just used the same values from company Rep.
*/
// update representative.marketing values based on related checkbox
useEffect(() => {
const handleCheckboxChange = (isChecked, fieldName) => {
const repInfo = isChecked
? formik.values.representative.member
: formik.values.representative[fieldName];
const newValues = {
...repInfo,
sameAsCompany: isChecked,
};
formik.setFieldValue(`representative.${fieldName}`, newValues);
};
const handleMemberInputChange = (value, name) => {
const memberRepInfo = {
...formik.values.representative.member,
[name]: value,
};
formik.setFieldValue('representative.member', memberRepInfo);
// update representative.marketing values based on related checkbox
if (isMarketingSameAsCompany) {
const newValues = {
...formik.values.representative.member,
...memberRepInfo,
id: formik.values.representative.marketing.id || '',
sameAsCompany: formik.values.representative.marketing.sameAsCompany,
sameAsCompany: isMarketingSameAsCompany,
};
formik.setFieldValue('representative.marketing', newValues);
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [isMarketingSameAsCompany, formik.values.representative.member]);
// update representative.accounting values based on related checkbox
useEffect(() => {
// update representative.accounting values based on related checkbox
if (isAccountingSameAsCompany) {
const newValues = {
...formik.values.representative.member,
...memberRepInfo,
id: formik.values.representative.accounting.id || '',
sameAsCompany: formik.values.representative.accounting.sameAsCompany,
sameAsCompany: isAccountingSameAsCompany,
};
formik.setFieldValue('representative.accounting', newValues);
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [isAccountingSameAsCompany, formik.values.representative.member]);
};
const generateContacts = (
representativeFields,
......@@ -79,15 +92,19 @@ const Contacts = ({ formik }) => {
placeholder={el.placeholder}
requiredMark={true}
disableInput={disableInput}
onChange={formik.handleChange}
value={formik.values.representative[`${type}`][`${el.name}`]}
onChange={
type === 'member'
? (ev) => handleMemberInputChange(ev.target.value, el.name)
: formik.handleChange
}
value={formik.values.representative?.[type]?.[el.name]}
error={
formik.touched.representative?.[`${type}`]?.[`${el.name}`] &&
Boolean(formik.errors.representative?.[`${type}`]?.[`${el.name}`])
formik.touched.representative?.[type]?.[el.name] &&
Boolean(formik.errors.representative?.[type]?.[el.name])
}
helperText={
formik.touched.representative?.[`${type}`]?.[`${el.name}`] &&
formik.errors.representative?.[`${type}`]?.[`${el.name}`]
formik.touched.representative?.[type]?.[el.name] &&
formik.errors.representative?.[type]?.[el.name]
}
/>
</div>
......@@ -126,7 +143,9 @@ const Contacts = ({ formik }) => {
name="representative.marketing.sameAsCompany"
color="primary"
checked={formik.values.representative.marketing.sameAsCompany}
onChange={formik.handleChange}
onChange={(ev) =>
handleCheckboxChange(ev.target.checked, 'marketing')
}
/>
}
label="Same as member rep."
......@@ -150,7 +169,9 @@ const Contacts = ({ formik }) => {
name="representative.accounting.sameAsCompany"
color="primary"
checked={isAccountingSameAsCompany}
onChange={formik.handleChange}
onChange={(ev) =>
handleCheckboxChange(ev.target.checked, 'accounting')
}
/>
}
label="Same as member rep."
......
......@@ -45,6 +45,12 @@ export default function Main() {
});
};
const updateSigningAuthorityForm = (values) => {
values.forEach((item) => {
formikSigningAuthority.setFieldValue(item.field, item.value);
});
};
const formikCompanyInfo = useFormik({
initialValues: initialValues,
validationSchema: validationSchema[0],
......@@ -70,15 +76,38 @@ export default function Main() {
setUpdatedFormValues(theNewValue);
console.log('updated company info: ', values);
const valueToUpdateFormik = [
const valueForMembershipLevelFormik = [
{ field: 'purchasingAndVAT', value: purchasingAndVAT },
{ field: 'membershipLevel', value: membershipLevel },
{ field: 'membershipLevel-label', value: membershipLevelLabel },
];
// set valueToUpdateFormik to membershipLevel formik to make sure the value is up to date
updateMembershipLevelForm(valueToUpdateFormik);
updateMembershipLevelForm(valueForMembershipLevelFormik);
executeSendDataByStep(1, theNewValue, currentFormId, currentUser.name);
const valueForSigningAuthorityFormik = [
{
field: 'signingAuthorityRepresentative',
value: signingAuthorityRepresentative,
},
];
updateSigningAuthorityForm(valueForSigningAuthorityFormik);
const setFieldValueObj = {
fieldName: {
organization: 'organization',
member: 'representative.member',
accounting: 'representative.accounting',
marketing: 'representative.marketing',
},
method: formikCompanyInfo.setFieldValue,
};
executeSendDataByStep(
1,
theNewValue,
currentFormId,
currentUser.name,
setFieldValueObj
);
goToNextStep(1, '/membership-level');
},
......@@ -120,7 +149,17 @@ export default function Main() {
setUpdatedFormValues({ ...updatedFormValues, workingGroups });
console.log('updated working groups: ', values);
executeSendDataByStep(3, values, currentFormId, currentUser.name);
const setFieldValueObj = {
fieldName: 'workingGroups',
method: formikWorkingGroups.setFieldValue,
};
executeSendDataByStep(
3,
values,
currentFormId,
currentUser.name,
setFieldValueObj
);
goToNextStep(3, '/signing-authority');
},
......@@ -147,7 +186,20 @@ export default function Main() {
];
// set valueToUpdateFormik to CompanyInfo formik to make sure the value is up to date
updateCompanyInfoForm(valueToUpdateFormik);
executeSendDataByStep(4, values, currentFormId, currentUser.name);
const setFieldValueObj = {
fieldName: 'signingAuthorityRepresentative',
method: {
signingAuthority: formikSigningAuthority.setFieldValue,
companyInfo: formikCompanyInfo.setFieldValue,
},
};
executeSendDataByStep(
4,
values,
currentFormId,
currentUser.name,
setFieldValueObj
);
goToNextStep(4, '/review');
},
......@@ -175,8 +227,7 @@ export default function Main() {
return (
<div className="container eclipseFdn-membership-webform">
<>
{window.location.hash === '/' ||
window.location.hash === '#sign-in' ? (
{window.location.hash === '/' || window.location.hash === '#sign-in' ? (
<SignInIntroduction />
) : null}
......@@ -256,6 +307,7 @@ export default function Main() {
<Redirect to={furthestPage.pathName} />
)}
</Route>
<Route path="/submitted">
{furthestPage.index >= 6 ? (
<SubmitSuccess />
......
......@@ -25,10 +25,6 @@ const MembershipLevel = ({ formik }) => {
const { membershipLevel } = formField;
const classes = useStyles();
// Fetch data only once and prefill data, as long as
// currentFormId, membershipLevel.name and setFieldValue
// Function does not change, will not cause re-render again
return (
<form onSubmit={formik.handleSubmit}>
<div className="align-center">
......
import CustomStepButton from '../../UIComponents/Button/CustomStepButton';
import Input from '../../UIComponents/Inputs/Input';
import { formField } from '../../UIComponents/FormComponents/formFieldModel';
import { useEffect } from 'react';
/**
* Have not added any API calls here,
* simply use the form fields to render
......@@ -9,18 +9,9 @@ import { useEffect } from 'react';
*/
const sectionName = 'signing-authority';
const SigningAuthority = ({ formik, updatedFormValues }) => {
const SigningAuthority = ({ formik }) => {
const { signingAuthorityRepresentative } = formField;
useEffect(() => {
formik.setFieldValue(
'signingAuthorityRepresentative',
updatedFormValues.signingAuthorityRepresentative
);
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
return (
<form onSubmit={formik.handleSubmit}>
<h1 className="fw-600 h2" id={sectionName}>
......
......@@ -48,7 +48,6 @@ const ParticipationLevel = ({
// the Set will deduplicate participation_levels options