I have a login component which unnecessarily re-renders. I have wrapped the component with React.memo and am using the useCallBack hooks to prevent those functions from getting created on every render if there values don't change...
Consider the following:
I have a generic FormComponent which gets one prop:
function FormComponent({
formType
}) { ....}
export default React.memo(FormComponent)
That prop will switch between the different forms I have e.g. login, registration etc. In my example I am just showing the login form, I figure I'll just apply the solution to the others.
function FormComponent({
formType
}) {
/* various setstate removed for brevity */
const Forms = {
Login: [LoginForm,
() => loginSubmit(
email,
password,
setEmail,
setPassword,
setFormError,
setFormSuccess,
setIsLoading,
setResponseMessage,
dispatch,
router,
user,
mutate
)
]
};
function handleChangeForUseCallBack(name, value) {
setResponseMessage('');
setPasswordFeedback('')
setPasswordConfirmationFeedback('')
setFormError(false);
setFormSuccess(false);
setEmailError(false);
setPasswordError(false);
setPasswordConfirmationError(false);
setDisableButton(false);
dispatch({ type: 'resetUserAccountIsVerified', })
setEmailDup(false);
if (value === '') setDisableButton(() => true)
if (name === 'email') {
setEmail(value);
}
if (name === 'password') {
setPassword(value);
}
if (name === 'password_confirmation') {
setPasswordConfirmation(value);
}
if (name === 'current_location') {
setCurrentLocation(value);
}
if (name === 'current_destination') {
setCurrentDestination(value);
}
if (name === 'interested_activities') {
setInterestedActivitiesInput(value);
}
}
const handleChange = useCallback((e) => {
e.persist();
const { name, value } = e.target;
handleChangeForUseCallBack(name, value);
}, [email, formType, password, password_confirmation, handleChangeForUseCallBack, setIsLoading]);
function handleSubmitForUseCallBack(e, form) {
e.preventDefault();
setDisableButton(true);
validateInputs(
form,
email,
setEmailError,
setEmailFeedback,
password,
password_confirmation,
setPasswordConfirmationError,
setPasswordConfirmationFeedback,
setPasswordError,
setPasswordFeedback,
setFormSuccess,
setFormError,
);
return preventSubmit ? false : Forms[form][1]()
}
const handleSubmit = useCallback((e, form) => {
handleSubmitForUseCallBack(e, form);
}, [email, password, password_confirmation, interestedActivities, handleSubmitForUseCallBack]);
function LoginForm() {
useEffect(() => {
dispatch({ type: 'resetUserAccountIsVerified' })
}, [id]);
return (
mounted && <GenericFormComponent
handleSubmit={handleSubmit}
formType={formType}
formSuccess={formSuccess}
formError={formError}
accountNotVerified={accountNotVerified}
email={email}
emailError={emailError}
emailFeedback={emailFeedback}
handleChange={handleChange}
password={password}
passwordError={passwordError}
passwordFeedback={passwordFeedback}
disableButton={disableButton}
buttonName="Log-in"
isLoading={isLoading}
setIsLoading={setIsLoading}
responseMessage={responseMessage}
/>
);
}
return Forms[formType][0]();
}
Is the problem the handleSubmit has a various function calls and values being passsed in which need to be passed into the useCallback
dependencies?
Any help would be appreciated!
I don't think
useCallback
is the issue here. One potential cause may be the following side effect running when theLoginForm
is rendered:but I may be wrong. I think the definite issue here is the fact that
LoginForm
is defined inside of yourFormComponent
. Since its defined inside ofFormComponent
each timeFormComponent
is reevaluated (from a state change, for example),LoginForm
would be reinitialized, and thus if it's already rendered, then it will rerender. I think defining theLoginForm
elsewhere would solve your problem.