I am using Next.js 14, and currently on a client component. This component has a multi-step form. In the final step, I trigger a server action to get a response and return feedback.
When the user clicks the Create Account
button, they should navigate to the Finalization
tab with the index 4, which shows a loading state until the server action returns a response. Then it shows a message to the user.
The process works well when using a fake timeout and fake response to emulate a test. However, when we actually trigger the server action (const data = await register(information);
), the function immediately resets the whole component’s state values to their initial values, as if the page was refreshed (but it wasn’t). On the second button click, it works fine.
This is the submitAction
function in sign-up-page.tsx
‘s Register Form Component:
const submitAction = async (
values: RegistrationDataType['accountSecurity'],
): Promise<ApiResponse | AuthError | undefined> => {
try {
setInformation((prev) => ({
...prev,
accountSecurity: values,
}));
setError(null);
setSuccess(null);
setIsLoading(true);
forward();
const data = await register(information);
if (data.success) {
console.log('Registration successful:', data);
setSuccess(data);
setError(null);
} else if (data.error) {
console.log('Registration error:', data);
setError(data);
setSuccess(null);
} else {
console.log('Registration unknown error:', data);
setError({
code: 'unknown_error',
message: 'An unknown error occurred.',
description: 'Oops! Something went wrong. Please try again.',
status: 400,
});
setSuccess(null);
}
return data;
} catch (error: ApiResponse | AuthError | any) {
console.error(
'%cERROR',
'background: #f43f5e; color: #fff1f2; padding: 2px;',
'An error occurred while registering the user: ',
error,
);
setError({
code: error.code ?? 'unknown_error',
message: error.message ?? 'An unknown error occurred.',
description: 'Oops! Something went wrong. Please try again.',
status: 400,
});
return error;
} finally {
setIsLoading(false);
forward();
}
};
And then passed to the Account Security tab in the following code block:
{selectedIndex === 3 && (
<div className='flex flex-col items-center justify-center w-full gap-5'>
<AccountSecurity
accountSecurity={information.accountSecurity}
submitAccountSecurity={submitAction}
isLoading={isLoading}
forward={forward}
backward={backward}
/>
</div>
)}
{selectedIndex === 4 && (
<div className='flex flex-col items-center justify-center w-full gap-5'>
<Finalization
information={information}
isLoading={isLoading}
isSuccess={hasSuccess !== null}
success={hasSuccess}
isError={hasError !== null}
error={hasError}
forward={forward}
backward={backward}
/>
</div>
)}
This is the State Initialisation block:
// ? States:
const [hasError, setError] = useState<ApiResponse | AuthError | null>(null);
const [hasSuccess, setSuccess] = useState<ApiResponse | null>(null);
// ? Router:
const router = useRouter();
// * Tabs:
const [selectedIndex, setSelectedIndex] = useState<number>(3);
// ? Information:
const [information, setInformation] = useState<RegistrationDataType>(registrationInitialData);
// - Transition:
const [isPending, startTransition] = useTransition();
// Loading state:
const [isLoading, setIsLoading] = useState(false);
Expected Behavior: When the Create Account button is clicked, it should navigate to the Finalization tab and display the loading state until the server action returns a response.
Actual Behavior: The form resets to the initial state on the first click, as if the page was refreshed. On the second click, it works fine.
Additional Context: The issue does not occur when using a fake timeout and fake response.
Only happens when the server action is triggered.
What I’ve Tried:
- Ensured state is preserved during the server action.
- Moved forward() calls to after the server action completes.
- Verified component structure and keys.
How can I fix this issue?
Any help would be appreciated!