UNPKG

react-server-actions

Version:

A package for working with actions in React and Next.js

97 lines 3.44 kB
'use client'; import React, { useEffect, useRef } from 'react'; import { z } from 'zod'; import { dateToInputDefaultValue, getZodValidationAttributes, } from './helpers.js'; const FormContext = React.createContext({ state: undefined, schema: undefined, debug: false }); export const useForm = () => { const context = React.useContext(FormContext); if (!context.state || !context.schema) return { state: undefined, schema: undefined, debug: false }; return { state: context.state, schema: context.schema, debug: context.debug, }; }; const FieldContext = React.createContext({ name: '', id: '', }); export const useField = () => { 'use no memo'; // the useField hook should not be memoized because the value will change const { name, id } = React.useContext(FieldContext); const { state, schema, debug } = useForm(); if (!state || !schema) throw new Error('<FormField> must be used within a <Form>'); // Get validation attributes from schema const { type, attrs } = getZodValidationAttributes(schema, [name]); // Create the field object const field = { invalid: state.invalid?.[name], value: state.formData?.[name], input: { id: id, name: name, 'aria-invalid': !!state.invalid?.[name], autoComplete: undefined, ...attrs, }, }; // Set the default value for mantaining the state across submissions let defaultValue = state.formData?.[name]; if (defaultValue && defaultValue instanceof Date) { defaultValue = dateToInputDefaultValue(defaultValue); } if (type === 'enum') { field.input.defaultValue = defaultValue; // TODO: This is not working if the input is a radio // if the input is a radio, we don't know which of the inputs is this one } else if (type === 'boolean') { if (defaultValue) { field.input.defaultChecked = defaultValue; } else { field.input.defaultChecked = false; } } else { field.input.defaultValue = defaultValue; } // Set autocomplete for string inputs if (type === 'string') { field.input.autoComplete = 'on'; } if (debug) { console.log(field); } return field; }; export function Form({ children, action, state, schema, className, reset, onSuccess, onError, debug, ...props }) { const formRef = useRef(null); if (reset !== false) { if (formRef.current && state.success) { formRef.current.reset(); } } useEffect(() => { if (state.success) { onSuccess?.(state.successData, state.formData); } else if (state.error) { onError?.(state.error); } }, [state, onSuccess, onError]); return (React.createElement(FormContext.Provider, { value: { state, schema, debug } }, React.createElement("form", { action: action, ref: formRef, className: className, ...props }, children))); } export function FormField({ render, name, }) { const id = React.useId(); return (React.createElement(FieldContext.Provider, { value: { name: name, id } }, React.createElement(FormFieldRenderer, { render: render }))); } function FormFieldRenderer({ render, }) { const field = useField(); return render(field); } //# sourceMappingURL=index.js.map