diff --git a/client/common/useSyncFormTranslations.js b/client/common/useSyncFormTranslations.js new file mode 100644 index 000000000..411c94236 --- /dev/null +++ b/client/common/useSyncFormTranslations.js @@ -0,0 +1,22 @@ +import { useEffect } from 'react'; + +// Usage: useSyncFormTranslations(formRef, language) +// This hook ensures that form values are preserved when the language changes. +// Pass a ref to the form instance and the current language as arguments. +const useSyncFormTranslations = (formRef, language) => { + useEffect(() => { + const form = formRef.current; + if (!form) return; + + const { values } = form.getState(); + form.reset(); + + Object.keys(values).forEach((field) => { + if (values[field]) { + form.change(field, values[field]); + } + }); + }, [language]); +}; + +export default useSyncFormTranslations; diff --git a/client/modules/User/components/LoginForm.jsx b/client/modules/User/components/LoginForm.jsx index becf41137..eb6eac840 100644 --- a/client/modules/User/components/LoginForm.jsx +++ b/client/modules/User/components/LoginForm.jsx @@ -1,4 +1,4 @@ -import React, { useState } from 'react'; +import React, { useRef, useState } from 'react'; import { useTranslation } from 'react-i18next'; import { Form, Field } from 'react-final-form'; import { useDispatch } from 'react-redux'; @@ -6,94 +6,109 @@ import { AiOutlineEye, AiOutlineEyeInvisible } from 'react-icons/ai'; import Button from '../../../common/Button'; import { validateLogin } from '../../../utils/reduxFormUtils'; import { validateAndLoginUser } from '../actions'; +import { useSyncFormTranslations } from '../../../common/useSyncFormTranslations'; function LoginForm() { - const { t } = useTranslation(); + const { t, i18n } = useTranslation(); const dispatch = useDispatch(); function onSubmit(formProps) { return dispatch(validateAndLoginUser(formProps)); } const [showPassword, setShowPassword] = useState(false); + const formRef = useRef(null); + const handleVisibility = () => { setShowPassword(!showPassword); }; + useSyncFormTranslations(formRef, i18n.language); + return (
- {({ handleSubmit, submitError, submitting, modifiedSinceLastSubmit }) => ( - - - {(field) => ( -
- - - {field.meta.touched && field.meta.error && ( - - {field.meta.error} - - )} -
- )} -
- - {(field) => ( -
- -
+ {({ + handleSubmit, + submitError, + submitting, + modifiedSinceLastSubmit, + form + }) => { + formRef.current = form; + + return ( + + + {(field) => ( +
+ - + {field.meta.touched && field.meta.error && ( + + {field.meta.error} + + )} +
+ )} +
+ + {(field) => ( +
+ +
+ + +
+ {field.meta.touched && field.meta.error && ( + + {field.meta.error} + + )}
- {field.meta.touched && field.meta.error && ( - - {field.meta.error} - - )} -
+ )} + + {submitError && !modifiedSinceLastSubmit && ( + {submitError} )} - - {submitError && !modifiedSinceLastSubmit && ( - {submitError} - )} - - - )} + + + ); + }} ); } diff --git a/client/modules/User/components/LoginForm.unit.test.jsx b/client/modules/User/components/LoginForm.unit.test.jsx index 28fb9383c..2f4262c16 100644 --- a/client/modules/User/components/LoginForm.unit.test.jsx +++ b/client/modules/User/components/LoginForm.unit.test.jsx @@ -23,6 +23,10 @@ jest.mock('../actions', () => ({ ) })); +jest.mock('../../../common/useSyncFormTranslations', () => ({ + useSyncFormTranslations: jest.fn() +})); + const subject = () => { reduxRender(, { store diff --git a/client/modules/User/components/SignupForm.jsx b/client/modules/User/components/SignupForm.jsx index 75ab6910e..14390e6d3 100644 --- a/client/modules/User/components/SignupForm.jsx +++ b/client/modules/User/components/SignupForm.jsx @@ -1,4 +1,4 @@ -import React, { useState } from 'react'; +import React, { useRef, useState } from 'react'; import { useTranslation } from 'react-i18next'; import { Form, Field } from 'react-final-form'; import { useDispatch } from 'react-redux'; @@ -7,6 +7,7 @@ import { validateSignup } from '../../../utils/reduxFormUtils'; import { validateAndSignUpUser } from '../actions'; import Button from '../../../common/Button'; import apiClient from '../../../utils/apiClient'; +import { useSyncFormTranslations } from '../../../common/useSyncFormTranslations'; function asyncValidate(fieldToValidate, value) { if (!value || value.trim().length === 0) { @@ -34,7 +35,7 @@ function validateEmail(email) { } function SignupForm() { - const { t } = useTranslation(); + const { t, i18n } = useTranslation(); const dispatch = useDispatch(); function onSubmit(formProps) { @@ -42,12 +43,14 @@ function SignupForm() { } const [showPassword, setShowPassword] = useState(false); const [showConfirmPassword, setShowConfirmPassword] = useState(false); + const formRef = useRef(null); const handleVisibility = () => { setShowPassword(!showPassword); }; const handleConfirmVisibility = () => { setShowConfirmPassword(!showConfirmPassword); }; + useSyncFormTranslations(formRef, i18n.language); return (
- {({ handleSubmit, pristine, submitting, invalid }) => ( - - - {(field) => ( -
- - - {field.meta.touched && field.meta.error && ( - - {field.meta.error} - - )} -
- )} -
- - {(field) => ( -
- - - {field.meta.touched && field.meta.error && ( - - {field.meta.error} - - )} -
- )} -
- - {(field) => ( -
- -
+ {({ handleSubmit, pristine, submitting, invalid, form }) => { + formRef.current = form; + return ( + + + {(field) => ( +
+ - + {field.meta.touched && field.meta.error && ( + + {field.meta.error} + + )}
- {field.meta.touched && field.meta.error && ( - - {field.meta.error} - - )} -
- )} - - - {(field) => ( -
- -
+ )} + + + {(field) => ( +
+ - + {field.meta.touched && field.meta.error && ( + + {field.meta.error} + + )}
- {field.meta.touched && field.meta.error && ( - - {field.meta.error} - - )} -
- )} - - - - )} + )} + + + {(field) => ( +
+ +
+ + +
+ {field.meta.touched && field.meta.error && ( + + {field.meta.error} + + )} +
+ )} +
+ + {(field) => ( +
+ +
+ + +
+ {field.meta.touched && field.meta.error && ( + + {field.meta.error} + + )} +
+ )} +
+ + + ); + }} ); }