UNPKG

next-form-action

Version:

A TypeScript library for handling form actions in Next.js applications

133 lines (125 loc) 4.17 kB
'use strict'; const React = require('react'); const navigation = require('next/navigation'); function _interopDefaultCompat (e) { return e && typeof e === 'object' && 'default' in e ? e.default : e; } const React__default = /*#__PURE__*/_interopDefaultCompat(React); class ActionResponse extends Error { payload; success; formErrors; extra; redirect; refresh; constructor(message, success, params = {}) { super(message); this.success = success; this.formErrors = params.formErrors; this.extra = params.extra; this.redirect = params.redirect; this.refresh = params.refresh; } toResponse(formData) { return { payload: formData, success: this.success, message: this.message, formErrors: this.formErrors, extra: this.extra, redirect: this.redirect, refresh: this.refresh }; } } class ActionError extends ActionResponse { constructor(message, params = {}) { super(message, false, params); } } class ActionSuccess extends ActionResponse { constructor(message, params = {}) { super(message, true, params); } } const createActionState = (payload = new FormData(), params = {}) => { return { ...params, payload }; }; function error(message, params = {}) { throw new ActionError(message, params); } function success(message, params = {}) { throw new ActionSuccess(message, params); } const createAction = (context, handler) => { return async (state, formData) => { try { return await handler(state, formData); } catch (error2) { if (error2 instanceof ActionResponse) { return error2.toResponse(formData); } if (error2 && typeof error2 === "object" && "digest" in error2 && typeof error2.digest === "string" && error2.digest.startsWith("NEXT_")) { throw error2; } console.error(`Error in form action "${context}":`, error2); const message = error2 instanceof Error && error2.message ? error2.message : "An unexpected error occurred. Please try again."; return new ActionError(message).toResponse(formData); } }; }; const useAction = (action, actionState) => { const router = navigation.useRouter(); const formRef = React.useRef(null); const [state, dispatch, isPending] = React.useActionState(action, actionState || createActionState()); const onSuccessCallbackRef = React.useRef(null); const onErrorCallbackRef = React.useRef(null); const onSubmitCallbackRef = React.useRef(null); React.useEffect(() => { if (state.success === true && onSuccessCallbackRef.current) { onSuccessCallbackRef.current(state); onSuccessCallbackRef.current = null; } else if (state.success === false && state.message && onErrorCallbackRef.current) { onErrorCallbackRef.current(state); onErrorCallbackRef.current = null; } if (state.redirect) router.push(state.redirect); if (state.refresh) router.refresh(); }, [state, router]); const FormError = (...props) => { return state.message ? /* @__PURE__ */ React__default.createElement("div", { ...props }, state.message) : null; }; const Form = ({ children, ...props }) => /* @__PURE__ */ React__default.createElement("form", { ...props, ref: formRef, action: enhancedDispatch }, children); const onFormSuccess = (callback) => { onSuccessCallbackRef.current = callback; }; const onFormError = (callback) => { onErrorCallbackRef.current = callback; }; const onFormSubmit = (callback) => { onSubmitCallbackRef.current = callback; }; const enhancedDispatch = async (formData) => { if (onSubmitCallbackRef.current) { await onSubmitCallbackRef.current(formData); onSubmitCallbackRef.current = null; } dispatch(formData); }; return { Form, FormError, state, isPending, formRef, onFormSuccess, onFormError, onFormSubmit }; }; exports.ActionError = ActionError; exports.ActionResponse = ActionResponse; exports.ActionSuccess = ActionSuccess; exports.createAction = createAction; exports.createActionState = createActionState; exports.error = error; exports.success = success; exports.useAction = useAction;