Next.js Discord

Discord Forum

Callback after server action is successful

Answered
Spectacled bear posted this in #help-forum
Open in Discord
Spectacled bearOP
Hi everyone, I'm using NextJS 14 and looking for a way to add a callback after I have submitting a form using server action. The idea is to present some sort of alert or clear input field after the server action is successful.

This is what I come up with but to be honest it is ugly as hell. Any help is appreciated!

This is my client component

export const Example = () => {
  const [state, formAction] = useFormState(uploadFile, null);
  const [errorMessage, setErrorMessage] = useState<string | null>(null);

  const fileInputRef = useRef<HTMLInputElement>(null);

  useEffect(() => {
    if (!state) {
      return;
    }

    setErrorMessage(state.isError ? state.message : null);

    if (!state.isError && fileInputRef.current) {
      fileInputRef.current.value = "";
    }
  }, [state]);

  return (
    <div>
      {errorMessage && <p>{errorMessage}</p>}
      <form action={formAction}>
        <input name="file" ref={fileInputRef} type="file"/>
      </form>
    </div>
  );
};



This is my action
export const uploadFile = async (
  prevState: {
    isError: boolean;
    message: string;
  } | null,
  formData: FormData
) => {
  "use server";
  const files = formData.get("file") as File;

  try {
    // upload file here
    return { isError: false, message: "Success" };
  } catch (e) {
    return { isError: true, message: "Failed to Upload" };
  }
};
Answered by Ray
export const Example = () => {
  const [state, setState] = useState({});
  const formRef = useRef<HTMLFormElement>(null)

  return (
    <div>
      {state?.message && <p>{state?.message}</p>}
      <form ref={formRef} action={async (data) => {
        const result = await uploadFile(data)
        setState(result)
        formRef.current?.reset()
      }} className="w-full">
        <input type="file"/>
      </form>
    </div>
  );
};
View full answer

5 Replies

export const Example = () => {
  const [state, formAction] = useFormState(uploadFile, null);
  const formRef = useRef<HTMLFormElement>(null)

  return (
    <div>
      {state.message && <p>{state.message}</p>}
      <form ref={formRef} action={async () => {
        await formAction()
        formRef.current?.reset()
      }} className="w-full">
        <input type="file"/>
      </form>
    </div>
  );
};
Spectacled bearOP
Thanks for the quick response Ray! but it seems that formAction is not a function that return promise, so await here does nothing :(. Furthermore, this approach only handles when the formAction completes, I wonder if we can trigger the clean up only when formAction succeeds?
ah use useState instead then
export const Example = () => {
  const [state, setState] = useState({});
  const formRef = useRef<HTMLFormElement>(null)

  return (
    <div>
      {state?.message && <p>{state?.message}</p>}
      <form ref={formRef} action={async (data) => {
        const result = await uploadFile(data)
        setState(result)
        formRef.current?.reset()
      }} className="w-full">
        <input type="file"/>
      </form>
    </div>
  );
};
Answer
Spectacled bearOP
You're awesome! Never thought we could directly call a server action like that. Much appreciated!