0.12.1 • Published 5 days ago

@gapu/formful v0.12.1

Weekly downloads
-
License
MIT
Repository
github
Last release
5 days ago

@gapu/formful - A fully type-safe form manager for react.js

Welcome to the documentation for @gapu/formful, a powerful form management library built on React and Zod. This library provides easy-to-use hooks and components for managing form state, validation, and submission.

Installation

To get started with @gapu/formful, install the package using npm or yarn:

npm install @gapu/formful zod
# or
yarn add @gapu/formful zod

After installation, you can import and use createForm and other hooks/components as shown in the usage examples throughout this documentation.

Usage examples

Reference

Usage Examples

Usage Example: Basic Form

This example demonstrates how to set up a simple form with basic fields like text inputs and a submit button. The form will include fields for a username and email, and it will perform basic validations.

Form Setup

First, define a Zod schema and the initial form state for the form:

import { z } from "zod";
import { createForm } from "@gapu/formful";

const userSchema = z.object({
  username: z.string().min(5, "Username must be at least 5 characters long"),
  email: z.string().email("Please enter a valid email")
});

const { useField, handleSubmit } = createForm({
  defaultState: { username: '', email: '' },
  schema: userSchema
});

Form Component

Create a React component to display the form and handle submission:

import React from 'react';

function BasicForm() {
  const { 
    value: username, 
    setValue: setUsername, 
    errors: usernameErrors 
  } = useField(state => state.username);
  const { 
    value: email, 
    setValue: setEmail, 
    errors: emailErrors 
  } = useField(state => state.email);

  const onSubmit = async (state) => {
    // Use the state provided by handleSubmit to submit form data
    alert('Form submitted with: ' + JSON.stringify(state));
  };

  return (
    <form onSubmit={(e) => {
      e.preventDefault();
      handleSubmit(onSubmit); // Pass onSubmit that expects the state
    }}>
      <div>
        <label htmlFor="username">Username:</label>
        <input
          id="username"
          type="text"
          value={username}
          onChange={(e) => setUsername(e.target.value)}
        />
        {usernameErrors.map((error, index) => (
          <div key={index} className="error-message">{error.message}</div>
        ))}
      </div>
      <div>
        <label htmlFor="email">Email:</label>
        <input
          id="email"
          type="email"
          value={email}
          onChange={(e) => setEmail(e.target.value)}
        />
        {emailErrors.map((error, index) => (
          <div key={index} className="error-message">{error.message}</div>
        ))}
      </div>
      <button type="submit">Submit</button>
    </form>
  );
}

Explanation

This basic form example shows how to create a straightforward user interface for data input with validations, providing immediate feedback and a clean method for form submission. This setup serves as a foundation for more complex form handling scenarios.


Usage Example: Arrays

This example demonstrates how to manage a form with an array of items. The form will allow users to dynamically add and remove entries from a list of tasks, each represented by a simple string.

Form Setup

First, define a Zod schema and the initial form state for an array of strings:

import { z } from "zod";
import { createForm } from "@gapu/formful";

const taskSchema = z.object({
  tasks: z.array(z.string())
});

const { useField, handleSubmit, setFieldValue } = createForm({
  defaultState: { tasks: ['Example Task 1'] },
  schema: taskSchema
});

Adding and Removing Tasks

Create functions to handle adding new tasks and removing existing ones:

const addTask = (newTask) => {
  setFieldValue(
    state => state.tasks, 
    currentTasks => [...currentTasks, newTask]
  );
};

const removeTask = (index) => {
  setFieldValue(
    state => state.tasks, 
    currentTasks => currentTasks.filter((_, idx) => idx !== index)
  );
};

Form Component

Create a React component to display the form, tasks, and controls for adding and removing tasks:

import React, { useState } from 'react';

function TaskManager() {
  const { value: tasks } = useField(state => state.tasks);
  const [newTask, setNewTask] = useState('');

  const handleSubmitNewTask = (e) => {
    e.preventDefault();
    if (newTask) {
      addTask(newTask);
      setNewTask('');
    }
  };

  return (
    <form onSubmit={handleSubmitNewTask}>
      <h3>Task List</h3>
      <ul>
        {tasks.map((task, index) => (
          <li key={index}>
            {task}
            <button type="button" onClick={() => removeTask(index)}>Remove</button>
          </li>
        ))}
      </ul>
      <input
        type="text"
        value={newTask}
        onChange={(e) => setNewTask(e.target.value)}
        placeholder="Add new task"
      />
      <button type="submit">Add Task</button>
    </form>
  );
}

Explanation

In this example:

  • Tasks are managed using an array within the form's state.
  • addTask appends a new task to the array.
  • removeTask filters out the task by its index.
  • The form component uses the `useField` hook to bind the tasks array to the UI and manipulate its state through user interactions.

This setup provides a dynamic and responsive user interface for managing a list of tasks with complete validation and state synchronization.


Usage Example: Localization and Internationalization

This example demonstrates how to implement a form that supports multiple languages, showcasing the localization and internationalization (i18n) capabilities within a React form managed by @gapu/formful. This involves dynamically displaying form labels, error messages, and other interface elements in different languages based on user preferences or settings.

Form Setup

First, let's set up a basic form with internationalization support using a simple i18n library (i18next). We'll create a Zod schema that doesn't change, but the messages displayed will vary based on the chosen language.

import { z } from "zod";
import i18n from 'i18next';
import { initReactI18next } from 'react-i18next';
import { createForm } from "@gapu/formful";

// Initialize i18n
i18n
  .use(initReactI18next)
  .init({
    resources: {
      en: {
        translation: {
          "name_required": "Name is required",
          "email_required": "Email is required",
          "email_invalid": "Email is invalid",
          "submit": "Submit",
          "name": "Name",
          "email": "Email"
        }
      },
      de: {
        translation: {
          "name_required": "Name ist erforderlich",
          "email_required": "E-Mail ist erforderlich",
          "email_invalid": "E-Mail ist ungültig",
          "submit": "Einreichen",
          "name": "Name",
          "email": "E-Mail"
        }
      }
    },
    lng: 'en',
    fallbackLng: 'en',
    interpolation: {
      escapeValue: false
    }
  });

const formSchema = z.object({
  name: z.string().min(1, { message: "name_required" }),
  email: z.string().email({ message: "email_invalid" }).min(1, { message: "email_required" })
});

const { useField, handleSubmit } = createForm({
  defaultState: {
    name: '',
    email: ''
  },
  schema: formSchema
});

// A function to translate error messages based on the current locale
function translateErrors(errors) {
  return errors.map(err => ({
    ...err,
    message: i18n.t(err.message)
  }));
}

Form Component with Dynamic Error Message Translation

import React from 'react';
import { useTranslation } from 'react-i18next';

function I18nForm() {
  const { t } = useTranslation();
  const { 
    value: name, 
    setValue: setName, 
    errors: nameErrors 
  } = useField(state => state.name);
  const { 
    value: email, 
    setValue: setEmail, 
    errors: emailErrors 
  } = useField(state => state.email);

  const onSubmit = async (state) => {
    console.log('Form submission state:', state);
  };

  return (
    <form onSubmit={(e) => {
      e.preventDefault();
      handleSubmit(onSubmit);
    }}>
      <div>
        <label>{t('name')}:</label>
        <input
          type="text"
          value={name}
          onChange={(e) => setName(e.target.value)}
        />
        {translateErrors(nameErrors).map((error, index) => (
          <div key={index} className="error-message">{error.message}</div>
        ))}
      </div>
      <div>
        <label>{t('email')}:</label>
        <input
          type="email"
          value={email}
          onChange={(e) => setEmail(e.target.value)}
        />
        {translateErrors(emailErrors).map((error, index) => (
          <div key={index} className="error-message">{error.message}</div>
        ))}
      </div>
      <button type="submit">{t('submit')}</button>
    </form>
  );
}

Explanation

  • Error Translation: After validation, error messages are translated according to the current language by the translateErrors function. This function modifies the list of errors from Zod, replacing the default error codes or messages with localized strings.
  • Dynamic Labels and Buttons: Uses the useTranslation hook to fetch translations for labels and buttons dynamically.

This approach keeps the validation logic clean and focused, while still enabling dynamic internationalization for user interfaces and messages.


Reference

createForm Documentation

The createForm function initializes a form management instance with specified default settings, validation schema, and optional parameters. It is the core function of this library, setting up the necessary infrastructure for state management, validation, and subscriber handling.

Parameters

CreateFormProps

  • Type: CreateFormProps<State, Schema>
  • Description: A configuration object containing the necessary parameters to create and manage the form.

Parameters Detail

  • defaultState: The initial data model of the form, representing the starting state.
  • defaultMetaState: The initial meta state of the form, representing which fields are initially touched, dirty, loading, etc.
  • schema: A Zod schema used for validating the form's data against the defined rules and structures.
  • storageKey (optional): A string key used for persisting form state in local storage or other external storage systems, aiding in state recovery and management across sessions.
  • logErrors (optional): A boolean flag to enable or disable error logging to the console. Useful for development and debugging.

Returns

  • Type: FormAPI<State, Schema>
  • Description: Returns an object containing methods and properties to manage and interact with the form.

Behavior

Upon execution, createForm constructs a form management context, including state tracking, initial state setup, and subscriber management. It provides handlers and utilities such as state getters/setters, submission handling, error management, and field-specific functionalities.

Usage Example

import { z } from "zod";
import { createForm } from "@gapu/formful";

const formSchema = z.object({
  username: z.string().min(5),
  email: z.string().email()
});

export const { 
  Field,
  useField,
  handleSubmit,
  setFieldValue,
  setInitialFieldValue,
  getErrors,
  getInitialErrors,
  useIsModified,
  isModified,
  isSubmitting,
  useIsSubmitting,
  getState,
  setState,
  getInitialState,
  setInitialState,
  getMetaState,
  setMetaState,
  subscribe,
  resetToInitial,
  resetToDefault,
  resetInitialToDefault
} = createForm({
  defaultState: { username: '', email: '' },
  defaultMetaState: {},
  schema: formSchema,
  storageKey: 'userForm',
  logErrors: true
});

Implementation Notes

  • Flexibility and Extensibility: Designed to be flexible and easily extensible, supporting various types of forms and validation patterns.
  • Error Handling: Integrates seamlessly with Zod for powerful and customizable validation error handling.
  • State Persistence: Optional state persistence using storageKey for enhanced user experience across sessions.

Field Documentation

The Field component is a higher-order React component, designed to manage and render individual form fields. It provides a way to handle field-specific state, validation errors, and changes.

Props

selector (optional)

  • Type: Selector<State, R>
  • Description: A function that selects the part of the form state that the field corresponds to. It determines what piece of the form state this Field component controls.
  • Default: Returns the entire form state.

children

  • Type: (props: ReturnType<typeof useField<R>>) => JSX.Element
  • Description: A render prop function that receives field-specific properties and returns JSX. This function allows you to customize the rendering of the form field and integrate it with your UI logic.

Returned Props from useField

When you use the Field component, the children function receives an object with the following properties:

value

  • Type: R
  • Description: The current value of the field.

setValue

  • Type: (update: Update<R>) => void
  • Description: A function to update the value of the field. It can accept a direct value or an updater function.

errors

  • Type: Array<ZodError>
  • Description: An array of validation errors for this field, derived from the Zod schema if the field's current state fails validation.

meta

  • Type: MetaState
  • Description: An object containing metadata about the field's state, such as touched, disabled, loading, etc.

setMeta

  • Type: (update: Update<MetaState>, replace: boolean = false) => void
  • Description: A function to update the metadata of the field. It can accept a direct value or an updater function. The optional replace parameter determines whether the update should replace the existing metadata or merge with it, by default it is set to merge.

isModified

  • Type: () => boolean
  • Description: A function that checks if the field's value has been modified from its initial value.

Usage Example

<Field selector={(state) => state.email}>
  {({ value, setValue, errors, meta, setMeta, isModified }) => (
    <div>
      <input
        type="email"
        value={value}
        onChange={(e) => setValue(e.target.value)}
        onFocus={() => setMeta({ touched: true })}
        className={errors.length > 0 ? 'error' : ''}
      />
      {isModified() && <span>(Modified)</span>}
      {meta.touched && errors.map((error, index) => (
        <div key={index} className="error-message">{error.message}</div>
      ))}
    </div>
  )}
</Field>

Implementation Notes

  • The Field component uses a memoization technique (typedMemo) to avoid unnecessary re-renders. It compares the selector and children props to determine if the component should update.

useField Documentation

The useField hook is used to manage the state and validation of individual form fields. It provides the necessary handlers and state variables to interface with the form's state.

Parameters

selector (optional)

  • Type: Selector<State, R>
  • Description: A function that selects the part of the form state that the field corresponds to. It determines what piece of the form state this useField hook controls.
  • Default: Returns the entire form state.

Returns

An object containing the following properties and functions:

value

  • Type: R
  • Description: The current value of the field.

setValue

  • Type: (update: Update<R>) => void
  • Description: A function to update the value of the field. It can accept a direct value or an updater function.

errors

  • Type: Array<ZodError>
  • Description: An array of validation errors for this field, derived from the Zod schema if the field's current state fails validation.

meta

  • Type: MetaState
  • Description: An object containing metadata about the field's state, such as touched, disabled, loading, etc.

setMeta

  • Type: (update: Update<MetaState>, replace: boolean = false) => void
  • Description: A function to update the metadata of the field. It can accept a direct value or an updater function. The optional replace parameter determines whether the update should replace the existing metadata or merge with it, by default it is set to merge.

isModified

  • Type: () => boolean
  • Description: A function that checks if the field's value has been modified from its initial value.

Usage Example

const { value, setValue, errors, meta, setMeta, isModified } = useField((state) => state.email);

return (
  <div>
    <input
      type="email"
      value={value}
      onChange={(e) => setValue(e.target.value)}
      onFocus={() => setMeta({ touched: true })}
      className={errors.length > 0 ? 'error' : ''}
    />
    {isModified() && <span>(Modified)</span>}
    {meta.touched && errors.map((error, index) => (
      <div key={index} className="error-message">{error.message}</div>
    ))}
  </div>
);

Implementation Notes

  • The useField hook utilizes the useSyncExternalStore hook to ensure the field's state remains synchronized with the external store provided by the library's state management system.
  • It leverages React's useCallback for optimized handling of the setValue and state check functions, preventing unnecessary re-renders and ensuring efficient performance even in complex forms with many fields.

handleSubmit Documentation

The handleSubmit function is used to manage the submission of form data, ensuring that all data is validated against the specified Zod schema before the submission is processed.

Parameters

onSubmit

  • Type: (state: State) => Promise<R>
  • Description: A function that is called to process the form data if validation succeeds. This function takes the current form state as its argument and should return a Promise that resolves to some result type R, which could be the outcome of a server request or any other asynchronous operation.

Returns

  • Type: Promise<R | undefined>
  • Description: Returns a promise that resolves to the result of the onSubmit function if the form data passes validation, or undefined if there are validation errors.
  • Note: If there are validation errors and logErrors is set to true, these errors are logged to the console.

Behavior

Validation

Before invoking the onSubmit callback function, handleSubmit checks for any validation errors in the current form state using the Zod schema. If any errors are found, the function returns immediately without calling onSubmit.

Submission State

The function sets a submitting state to true when the submission process starts, which can be monitored through the isSubmitting or useIsSubmitting hooks. Once the onSubmit function completes or fails, it sets submitting back to false.

Usage Example

const { handleSubmit, createForm } = useFormful({
  defaultState: { email: '', password: '' },
  schema: z.object({
    email: z.string().email(),
    password: z.string().min(8)
  })
});

const onSubmit = async (state) => {
  try {
    const response = await api.submitLogin(state);
    console.log('Login successful:', response);
    return response;
  } catch (error) {
    console.error('Login failed:', error);
  }
}

return (
  <form onSubmit={(e) => {
    e.preventDefault();
    handleSubmit(onSubmit);
  }}>
    {/* Form inputs and submit button */}
  </form>
);

Implementation Notes

  • Error Handling: If logErrors is enabled, validation errors are logged with detailed descriptions to aid in debugging.
  • State Management: Uses the form's state management system to toggle the submitting state appropriately, ensuring that the UI can react to changes in the submission status (e.g., by disabling the submit button).

setFieldValue Documentation

The setFieldValue function is used to programmatically update the value of a specific field in the form’s current state. It supports both direct value updates and functional updates, where the new value is determined based on the current field value.

Parameters

selector

  • Type: Selector<State, R>
  • Description: A function that identifies the part of the state to update. It selects the specific field whose value needs modification.

update

  • Type: Update<R>
  • Description: The new value for the field, or a function that returns the new value by accepting the current field value.

Returns

  • Type: void
  • Description: This function does not return any value but updates the specified field in the form's current state.

Behavior

Upon execution, setFieldValue modifies the state of the form at the specified field. If update is a function, it receives the current value of the field and must return the new value.

Usage Example

const { setFieldValue } = createForm({
  defaultState: { username: 'user', email: 'user@example.com' },
  schema: z.object({
    username: z.string(),
    email: z.string().email()
  })
});

// Directly setting a new email
setFieldValue(state => state.email, 'new@example.com');

// Functionally updating the username
setFieldValue(state => state.username, currentName => currentName.toUpperCase());

Implementation Notes

  • Dynamic Updates: Ideal for scenarios where field values need to be updated in response to specific events or conditions within the application.

setInitialFieldValue Documentation

The setInitialFieldValue function is similar to setFieldValue but specifically targets the initial state of the form. It is used when there is a need to update the baseline state from which modifications are detected.

Parameters

selector

  • Type: Selector<State, R>
  • Description: A function that selects the specific field in the initial state to update.

update

  • Type: Update<R>
  • Description: The new value for the field, or a function that returns the new value based on the current initial value.

Returns

  • Type: void
  • Description: This function updates the specified field in the form's initial state and does not return any value.

Behavior

setInitialFieldValue modifies the initial state, affecting how changes are considered when comparing the current state to its initial conditions.

Usage Example

const { setInitialFieldValue } = createForm({
  defaultState: { username: '', email: '' },
  schema: z.object({
    username: z.string(),
    email: z.string().email()
  })
});

// Directly setting a new email
setInitialFieldValue(state => state.email, 'initial@example.com');

// Functionally updating the username
setInitialFieldValue(state => state.username, initialName => initialName.toUpperCase());

Implementation Notes

  • Baseline Adjustment: Useful for situations where the initial state needs to be redefined, potentially in response to changes in application requirements or pre-loading different data.

getErrors Documentation

The getErrors function retrieves the current validation errors from the form state.

Returns

  • Type: z.ZodError<State> | null
  • Description: Returns a Zod error object containing details about any validation failures, or null if the state is valid.

Behavior

This function uses the Zod schema to validate the current state and returns any errors encountered during the validation. If no errors are found, it returns null.

Usage Example

const { getErrors } = createForm({
  defaultState: { username: 'user', email: 'user@example.com' },
  schema: z.object({
    username: z.string().min(3),
    email: z.string().email().nonempty()
  })
});

const currentErrors = getErrors();
if (currentErrors) {
  console.error('Current State Errors:', currentErrors.issues);
}

getInitialErrors Documentation

The getInitialErrors function provides retrieves validation errors from the initial state of the form.

Returns

  • Type: z.ZodError<State> | null
  • Description: Returns a Zod error object containing details about validation failures in the initial state, or null if the initial state is valid.

Usage Example

const { getInitialErrors } = createForm({
  defaultState: { username: 'user', email: 'user@example.com' },
  schema: z.object({
    username: z.string().min(3),
    email: z.string().email().nonempty()
  })
});

const initialErrors = getInitialErrors();
if (initialErrors) {
  console.error('Initial State Errors:', initialErrors.issues);
}

useIsModified Documentation

The useIsModified function is a React hook that determines whether the value of a specific field, or the entire form state if no specific field is provided, has been modified from its initial value.

Parameters

selector (optional)

  • Type: Selector<State, R>
  • Default: (state) => state (selects the entire state)
  • Description: A selector function that extracts the specific value or the entire state to be monitored for modifications.

Returns

  • Type: boolean
  • Description: Returns true if the selected field's or entire state's value has changed from the initial value; otherwise, returns false.

Behavior

This hook tracks changes to a specific field or the entire form state by comparing its current and initial values.

Usage Example

const { useIsModified } = createForm({
  defaultState: { username: 'user123', email: 'example@email.com' },
  schema: z.object({
    username: z.string(),
    email: z.string().email()
  })
});

function UserInfo() {
  const isModified = useIsModified(); // Checks if any part of the state is modified
  
  return (
    <div>
      User Information: (Modified: {isModified ? 'Yes' : 'No'})
    </div>
  );
}

Implementation Notes

  • Reactivity: This hook provides a reactive way to detect changes to field values or the entire form state, enabling UI components to respond to modifications dynamically.

isModified Documentation

The isModified function provides a non-reactive way to determine if the value of a specific field or the entire form state, if no field is specified, has been altered from its initial state.

Parameters

selector (optional)

  • Type: Selector<State, R>
  • Default: (state) => state (selects the entire state)
  • Description: A selector function that identifies the specific value or the entire state to check within the form's state.

Returns

  • Type: boolean
  • Description: Returns true if the selected field's or the entire state's value differs from its initial state; otherwise, returns false.

Behavior

This function compares the current and initial values of a field or the entire form state directly, providing an immediate check without reactivity. Suitable for use in functions where reactivity is not required.

Usage Example

const { isModified, getState } = createForm({
  defaultState: { username: 'user123', email: 'example@email.com' },
  schema: z.object({
    username: z.string(),
    email: z.string().email()
  })
});

const checkModification = () => {
  if (isModified()) { // Checks if any part of the state is modified
    console.log('Some part of the state has been modified.');
  }
};

Implementation Notes

  • Non-Reactive: Suitable for scenarios where you need to check the modification status of a field or the entire form state at a specific moment in time without causing re-renders.

useIsSubmitting Documentation

The useIsSubmitting function is a React hook designed to provide the current submission state of the form. It allows components to reactively update when the submission state changes.

Returns

  • Type: boolean
  • Description: Returns a boolean value indicating whether the form is currently in the submission process.

Behavior

This hook utilizes the useSyncExternalStore to subscribe to the submitting state store. It provides a reactive interface that re-renders the consuming component whenever the submission state changes.

Usage Example

const { useIsSubmitting } = createForm({
  defaultState: { name: '', email: '' },
  schema: z.object({
    name: z.string().min(1),
    email: z.string().email()
  })
});

function SubmitButton() {
  const isSubmitting = useIsSubmitting();
  
  return (
    <button type="submit" disabled={isSubmitting}>
      {isSubmitting ? 'Submitting...' : 'Submit'}
    </button>
  );
}

Implementation Notes

  • Reactivity: This hook ensures that any component using it will re-render whenever the form's submitting state changes, enabling dynamic UI updates based on the form's submission status.

isSubmitting Documentation

The isSubmitting function provides a non-reactive way to check the current submission state of the form. It is useful in contexts where reactivity is not required.

Returns

  • Type: boolean
  • Description: Returns a boolean value indicating whether the form is currently in the submission process.

Behavior

Directly returns the current value of the submitting state without setting up a subscription. This function should be used when you need to check the submission state in a non-reactive context, such as in event handlers or other functions.

Usage Example

const { isSubmitting, handleSubmit } = createForm({
  defaultState: { username: '', password: '' },
  schema: z.object({
    username: z.string().min(3),
    password: z.string().min(8)
  })
});

const handleSpecialCheck = () => {
  if (isSubmitting()) {
    console.log('Submission is in progress...');
  }
};

return (
  <button onClick={handleSpecialCheck}>Check Submission Status</button>
);

Implementation Notes

  • Non-Reactive: This function gives a snapshot of the submission state at the time of invocation, suitable for use cases where reactivity is not required or desired.

getState Documentation

The getState function retrieves the current state of the form. This function is essential for accessing the complete state at any given time without modifying it.

Returns

  • Type: State
  • Description: Returns the current state of the form as defined by the initial configuration and subsequent modifications through user interaction or programmatic updates.

Behavior

This function provides a direct, non-reactive snapshot of the form's state. It is ideal for situations where you need to access the state without causing a re-render or side effects.

Usage Example

const { getState } = createForm({
  defaultState: { username: 'user123', email: 'example@email.com' },
  schema: z.object({
    username: z.string(),
    email: z.string().email()
  })
});

const currentState = getState();
console.log('Current Form State:', currentState);

Implementation Notes

  • Non-Reactive: getState does not subscribe or listen to state changes, and therefore does not trigger any component re-renders when called.

setState Documentation

The setState function updates the current state of the form. It allows for both full and partial updates, providing flexibility in how the form state is managed over time.

Parameters

update

  • Type: Update<State>
  • Description: The new state value or an updater function that provides the new state based on the previous state. If an updater function is used, it should return the new state.

Returns

  • Type: void
  • Description: This function does not return any value.

Behavior

setState can be used to programmatically change the state of the form. This function is typically used in response to user actions or API calls that necessitate a change in form state.

Usage Example

const { setState } = createForm({
  defaultState: { username: '', password: '' },
  schema: z.object({
    username: z.string(),
    password: z.string().min(8)
  })
});

// Setting a new state directly
setState({ username: 'newUser', password: 'newPassword123' });

// Updating state based on previous state
setState(prevState => ({
  ...prevState,
  username: 'updatedUser'
}));

Implementation Notes

  • Flexibility: Supports both direct state setting and functional updates, allowing for complex state manipulations based on the previous state.

getInitialState Documentation

The getInitialState function retrieves the initial state of the form as set during the form's creation. This function is useful for accessing the base state before any user interactions or updates have been applied.

Returns

  • Type: State
  • Description: Returns the initial state of the form, reflecting the configuration at the time of form creation.

Behavior

This function offers a way to access the immutable baseline state of the form, enabling scenarios where a comparison to the original starting point is needed, or a reset to this state is intended.

Usage Example

const { getInitialState } = createForm({
  defaultState: { username: 'initialUser', email: 'initial@example.com' },
  schema: z.object({
    username: z.string(),
    email: z.string().email()
  })
});

const initialState = getInitialState();
console.log('Initial Form State:', initialState);

Implementation Notes

  • Immutable Reference: The initial state returned by this function is a reference to the form's starting state and should not be modified directly.

setInitialState Documentation

The setInitialState function updates the initial state of the form. This can be used to programmatically reset the form's baseline state to a new configuration.

Parameters

newInitialState

  • Type: State
  • Description: The new initial state for the form.

Returns

  • Type: void
  • Description: This function does not return any value.

Behavior

setInitialState provides a way to redefine the initial state of the form. This might be necessary if the form needs to adapt to new requirements or initial conditions after it has been created.

Usage Example

const { setInitialState } = createForm({
  defaultState: { username: '', email: '' },
  schema: z.object({
    username: z.string(),
    email: z.string().email()
  })
});

// Redefining the initial state
setInitialState({ username: 'newInitialUser', email: 'newInitial@example.com' });

Implementation Notes

  • State Reset: Ideal for scenarios where the entire form state needs to be reinitialized to a different starting point without affecting the current user-modified state.

subscribe Documentation

The subscribe function allows components or external entities to listen to state changes within the form. Subscribers will receive updates whenever the form's state changes, making it ideal for integrating custom logic or third-party services that need to react to state updates.

Parameters

subscriber

  • Type: Subscriber<SubscriberArg>
  • Description: A callback function that is called whenever the form state changes. This function receives an argument object containing the current state and any validation errors as SubscriberArg.

Returns

  • Type: () => void
  • Description: Returns an unsubscribe function that can be called to stop receiving state updates.

Behavior

Upon calling subscribe, the provided subscriber function is added to a set of listeners. Whenever the form's state changes, all subscribed listeners are invoked. The unsubscribe function returned when subscribing can be used to remove the listener from the set, preventing further calls.

Usage Example

const { subscribe } = createForm({
  defaultState: { username: 'user', email: 'user@example.com' },
  schema: z.object({
    username: z.string(),
    email: z.string().email()
  })
});

const unsubscribe = subscribe(({ state, errors }) => {
  console.log('Updated state:', state);
  if (errors) {
    console.error('Validation errors:', errors.issues);
  }
});

// Later in the code, to stop receiving updates
unsubscribe();

Implementation Notes

  • Event-driven Updates: subscribe leverages an event-driven model to inform subscribers of state changes, ensuring that external systems or components can react appropriately without polling the state.

resetToInitial Documentation

The resetToInitial function resets the form's current state back to its initial state. This is useful when you want to discard any modifications made by the user and revert to the state as it was at the time of form initialization.

Returns

  • Type: void
  • Description: This function does not return any value.

Behavior

Triggers a state update that sets the current state of the form to match the initial state. This operation is beneficial in scenarios where a user might want to start over without reloading the form.

Usage Example

const { resetToInitial } = createForm({
  defaultState: { username: 'user', email: 'user@example.com' },
  schema: z.object({
    username: z.string(),
    email: z.string().email()
  })
});

// Action to reset the form to its initial state
resetToInitial();

Implementation Notes

  • Reversion to Initial: Use this function to revert the form state to its initial configuration, particularly after modifications.

resetToDefault Documentation

The resetToDefault function resets the form's current state back to the default state specified at the time of form creation. This allows the form to be reset to the predefined default values, independent of the initial values set during form instantiation.

Returns

  • Type: void
  • Description: This function does not return any value.

Behavior

Updates the form's current state to the default state. This is particularly useful for scenarios where the form's initial state might have been dynamically adjusted post-creation, and there is a need to return to the originally intended default values.

Usage Example

const { resetToDefault } = createForm({
  defaultState: { username: '', email: '' },
  schema: z.object({
    username: z.string(),
    email: z.string().email()
  })
});

// Action to reset the form to default settings
resetToDefault();

Implementation Notes

  • Return to Defaults: Ideal for resetting the form state to start fresh with the default values as configured during the initial setup.

resetInitialToDefault Documentation

The resetInitialToDefault function resets the form's initial state to the default state. This is useful when the default state needs to become the new baseline for initial values, typically after significant changes or updates to the form's purpose or structure.

Returns

  • Type: void
  • Description: This function does not return any value.

Behavior

Sets the initial state of the form to the default state. This adjustment is crucial when the form's setup has evolved, and the initial state must align with new default parameters.

Usage Example

const { resetInitialToDefault } = createForm({
  defaultState: { username: 'admin', email: 'admin@example.com' },
  schema: z.object({
    username: z.string(),
    email: z.string().email()
  })
});

// Action to set the initial state to the default state
resetInitialToDefault();

Implementation Notes

  • Update Initial Baseline: Useful for scenarios where the default state is updated and needs to be reflected as the new starting point for initial values.

Type definitions

import { z, type ZodError } from 'zod';

type Update<S> = S | ((state: S) => S);
type Subscriber<S> = (state: S) => void;
type Selector<S, R> = (state: S) => R;
type MetaState = {
    touched?: boolean;
    dirty?: boolean;
    visible?: boolean;
    disabled?: boolean;
    loading?: boolean;
    valid?: boolean;
    focused?: boolean;
    readOnly?: boolean;
};
type CreateFormProps<State, Schema> = {
    defaultState: State;
    schema: Schema;
    defaultMetaState?: Record<string, MetaState>;
    storageKey?: string;
    logErrors?: boolean;
};
declare const createForm: <Schema extends z.ZodType<any, z.ZodTypeDef, any>>({ 
  schema, 
  storageKey, 
  defaultState, 
  defaultMetaState, 
  logErrors 
}: CreateFormProps<z.TypeOf<Schema>, Schema>) => {
    Field: <R = z.TypeOf<Schema>>({ selector, children }: {
        selector?: Selector<z.TypeOf<Schema>, R> | undefined;
        children: (props: {
            value: R;
            setValue: (update: Update<R>) => void;
            errors: z.ZodIssue[];
            meta: MetaState;
            setMeta: (update: Update<MetaState>, replace?: boolean) => void;
            isModified: () => boolean;
        }) => JSX.Element;
    }) => JSX.Element;
    useField: <R_1 = z.TypeOf<Schema>>(selector?: Selector<z.TypeOf<Schema>, R_1>) => {
        value: R_1;
        setValue: (update: Update<R_1>) => void;
        errors: z.ZodIssue[];
        meta: MetaState;
        setMeta: (update: Update<MetaState>, replace?: boolean) => void;
        isModified: () => boolean;
    };
    handleSubmit: <R_2>(onSubmit: (state: z.TypeOf<Schema>) => Promise<R_2>) => Promise<R_2 | undefined>;
    setFieldValue: <R_3>(selector: Selector<z.TypeOf<Schema>, R_3>, update: Update<R_3>) => void;
    setInitialFieldValue: <R_3>(selector: Selector<z.TypeOf<Schema>, R_3>, update: Update<R_3>) => void;
    getErrors: () => ZodError<z.TypeOf<Schema>> | null;
    getInitialErrors: () => ZodError<z.TypeOf<Schema>> | null;
    useIsModified: <R_4 = z.TypeOf<Schema>>(selector?: Selector<z.TypeOf<Schema>, R_4>) => boolean;
    isModified: <R_5 = z.TypeOf<Schema>>(selector?: Selector<z.TypeOf<Schema>, R_5>) => boolean;
    isSubmitting: () => boolean;
    useIsSubmitting: () => boolean;
    getState: () => z.TypeOf<Schema>;
    setState: (update: Update<z.TypeOf<Schema>>) => void;
    getInitialState: () => z.TypeOf<Schema>;
    setInitialState: (update: Update<z.TypeOf<Schema>>) => void;
    getMetaState: () => Record<string, MetaState>;
    setMetaState: (update: Update<Record<string, MetaState>>) => void;
    subscribe: (s: Subscriber<{
        state: z.TypeOf<Schema>;
        errors: ZodError<z.TypeOf<Schema>> | null;
    }>) => () => boolean;
    resetToInitial: () => void;
    resetToDefault: () => void;
    resetInitialToDefault: () => void;
};

export { 
  type CreateFormProps, 
  type MetaState, 
  type Selector, 
  type Subscriber, 
  type Update, 
  createForm 
};
0.12.1

5 days ago

0.12.0

6 days ago

0.11.0

10 days ago

0.11.1

10 days ago

0.11.2

10 days ago

0.11.3

9 days ago

0.10.1

11 days ago

0.10.0

11 days ago

0.9.0

11 days ago

0.8.0

12 days ago

0.7.3

15 days ago

0.7.2

16 days ago

0.7.1

2 months ago

0.7.0

2 months ago

0.5.2

2 months ago

0.6.0

2 months ago

0.5.0

2 months ago

0.5.1

2 months ago

0.4.3

5 months ago

0.4.2

5 months ago

0.4.1

5 months ago

0.4.0

5 months ago

0.3.0

5 months ago

0.2.2

5 months ago

0.2.0

5 months ago

0.1.7

5 months ago

0.1.4

5 months ago

0.1.3

5 months ago

0.1.2

5 months ago

0.1.1

5 months ago

0.1.0

5 months ago