Reputation: 9
I have a simple html form in Next JS 14.2.2 (app router) application. The form has two submit buttons, lets says Button1 and Button2. I am using useFormStatus hook from react-dom to handle the loading indicator in each of the submit buttons.
When I am submitting the form by clicking on Button1, the loading indicator starts for both the buttons. I want only specific button to show loading indicator which was clicked. How can we handle this ?
I tried using name parameter on each of the buttons but that information is not present in the formStatus variable available from useFormStatus hook
Upvotes: 0
Views: 497
Reputation: 185
This has worked for me:
So the code for the button would look like this:
export function SubmitButton({ children, pendingText, ...props }: Props) {
const { pending } = useFormStatus();
const [buttonClicked, setButtonClicked] = useState(false);
return (
<Button type="submit" aria-disabled={pending} disabled={pending} onClick={() => setButtonClicked(true)} {...props}>
{pending && buttonClicked ? pendingText : children}
</Button>
);
}
Upvotes: 1
Reputation: 20626
There is no out of box solution for this. Hence there can be multiple ways to do this. One of them can be by having an extra state variable that helps let you know which was the button that was clicked.
Suppose below is your component UserNameForm
which is wrapped by the form component in the parent.
You can add a state variable which holds the value of the latest clicked button. Based on that you can set the value of pending for each individual button. The only thing worth noting is that you will have to add extra onClick handlers to each button, which you probably do not have earlier so this adds up to some code.
export default function UsernameForm() {
const [buttonClickState, setButtonClickState] = useState(null);
const { pending, data } = useFormStatus();
const updateButtonClickState = (buttonType) => {
setButtonClickState(buttonType);
};
useEffect(() => {
if (!pending) {
setButtonClickState(null);
}
}, [pending]);
return (
<div>
<h3>Request a Username: </h3>
<input type="text" name="username" disabled={pending} />
<button
type="submit"
disabled={buttonClickState === "submit" ? pending : false}
onClick={() => {
updateButtonClickState("submit");
}}
>
Submit
</button>
<button
type="submit"
disabled={buttonClickState === "save" ? pending : false}
onClick={() => {
updateButtonClickState("save");
}}
>
Save
</button>
<br />
<p>{data ? `Requesting ${data?.get("username")}...` : ""}</p>
</div>
);
}
The whole functionality can also be moved to a hook like this:
const useButtonStatus = ({ pending }) => {
const [buttonClickState, setButtonClickState] = useState(null);
useEffect(() => {
if (!pending) {
setButtonClickState(null);
}
}, [pending]);
const updateButtonClickState = (buttonType) => {
setButtonClickState(buttonType);
};
return {
updateButtonClickState,
submitDisabled: buttonClickState === "submit" ? pending : false,
saveDisabled: buttonClickState === "save" ? pending : false,
};
};
And then it can be easily decided which button to show as disabled:
<button
type="submit"
disabled={submitDisabled}
onClick={() => {
updateButtonClickState("submit");
}}
>
Submit
</button>
<button
type="submit"
disabled={saveDisabled}
onClick={() => {
updateButtonClickState("save");
}}
>
Save
</button>
Upvotes: 0