### GlobalFormaProvider Setup for Ehfuse Forma
Source: https://github.com/ehfuse/forma/blob/main/docs/en/examples/basic-example.md
Illustrates the necessary setup for using Ehfuse Forma hooks, specifically `useModal`. The application must be wrapped with `GlobalFormaProvider` at the top level to ensure context is available.
```typescript
import { GlobalFormaProvider } from "@ehfuse/forma";
function App() {
return (
);
}
```
--------------------------------
### Forma: Performance Tip - Memoization
Source: https://github.com/ehfuse/forma/blob/main/docs/en/getting-started.md
Provides a performance optimization strategy for Forma forms using `useMemo`. This example demonstrates how to memoize expensive calculations that depend on form values, ensuring they are only recomputed when necessary.
```tsx
import { useMemo } from "react";
// Assuming calculateExpensiveValue is a defined function
const expensiveValue = useMemo(() => {
return calculateExpensiveValue(form.useFormValue("data"));
}, [form.useFormValue("data")]);
```
--------------------------------
### Install Forma NPM Package
Source: https://github.com/ehfuse/forma/blob/main/docs/en/getting-started.md
Installs the Forma NPM package using either npm or yarn. This is the first step for integrating Forma into your project.
```bash
npm install @ehfuse/forma
# or
yarn add @ehfuse/forma
```
--------------------------------
### Install Forma Package
Source: https://github.com/ehfuse/forma/blob/main/docs/en/migration.md
Provides the npm command to install the Forma package, a necessary first step for migration.
```bash
npm install @ehfuse/forma
```
--------------------------------
### Shopping Cart Example with Forma
Source: https://github.com/ehfuse/forma/blob/main/docs/en/examples/basic-example.md
Illustrates a practical application of Forma for managing a shopping cart's state. It demonstrates how to add items, update the total, and efficiently subscribe to changes in the cart's item count.
```typescript
// Basic shopping cart component
function ShoppingCart() {
const cart = useGlobalFormaState({
stateId: "shopping-cart",
initialValues: {
items: [],
total: 0,
},
});
// ✅ Recommended: .length subscription (re-renders only when array length changes)
const itemCount = cart.useValue("items.length");
const total = cart.useValue("total");
return (
Shopping Cart ({itemCount})
Total: ${total}
);
}
// Product addition component
function ProductList() {
const cart = useGlobalFormaState({
stateId: "shopping-cart",
});
const addToCart = (product) => {
const currentItems = cart.getValues().items || [];
cart.setValue("items", [...currentItems, product]);
// Update total
const newTotal =
currentItems.reduce((sum, item) => sum + item.price, 0) +
product.price;
cart.setValue("total", newTotal);
};
return (
);
}
```
--------------------------------
### Reusability Example (TypeScript/JSX)
Source: https://github.com/ehfuse/forma/blob/main/docs/en/examples/todoapp-example.md
Illustrates how actions defined in the 'Actions' structure can be called from multiple UI elements, such as buttons and input fields, promoting code reuse and a consistent user experience.
```typescript
// Same action can be called from multiple places
e.key === "Enter" && state.actions.addTodo()} />
```
--------------------------------
### Testability of Actions Independently (Test)
Source: https://github.com/ehfuse/forma/blob/main/docs/en/examples/todoapp-example.md
Demonstrates the testability of individual actions using a testing framework. This example shows how to create a mock context and assert the outcome of an action, such as adding a todo item.
```Test
// Actions independently testable
test("addTodo should add new todo", () => {
const context = createMockContext();
actions.addTodo(context);
expect(context.values.todos.length).toBe(1);
});
```
--------------------------------
### Complete Global Form with Validation and Submission - TypeScript
Source: https://github.com/ehfuse/forma/blob/main/docs/en/examples/basic-example.md
Demonstrates a complete global form setup using useGlobalForm in TypeScript. It includes initial values, validation logic, submission handlers, and completion callbacks. This example shows how to define and use the form within a single component.
```typescript
function GlobalFormExample() {
const form = useGlobalForm({
formId: "user-form",
initialValues: { name: "", email: "", age: 0 },
onValidate: async (values) => {
// Name validation
if (!values.name.trim()) {
alert("Please enter your name.");
return false;
}
// Email validation
if (!values.email.includes("@")) {
alert("Please enter a valid email.");
return false;
}
return true;
},
onSubmit: async (values) => {
console.log("Global form submission:", values);
await api.submitUser(values);
},
onComplete: (values) => {
alert("Submission completed!");
},
});
return (
);
}
```
--------------------------------
### Multi-Step Form: Quick Submit Button Example
Source: https://github.com/ehfuse/forma/blob/main/docs/en/examples/basic-example.md
Demonstrates how a separate component can trigger the form submission process for a globally managed form. It accesses the form state using the `formId` and calls the `submit` method, leveraging handlers registered in other components.
```typescript
// 💡 Pro tip: Can submit from other components too!
function QuickSubmitButton() {
const form = useGlobalForm({
formId: "user-registration", // Same ID
// No handlers - automatically uses handlers registered in FinalStep
});
return (
);
}
```
--------------------------------
### Testability Example (TypeScript)
Source: https://github.com/ehfuse/forma/blob/main/docs/en/examples/todoapp-example.md
Highlights the testability advantage of the 'Actions' structure. Actions can be tested independently of the UI components, making unit testing more straightforward and robust.
```typescript
// Actions can be tested independently
test("addTodo should add new todo", () => {
const context = createMockContext();
actions.addTodo(context);
expect(context.values.todos.length).toBe(1);
});
```
--------------------------------
### Logic Encapsulation Example (TypeScript/JSX)
Source: https://github.com/ehfuse/forma/blob/main/docs/en/examples/todoapp-example.md
Demonstrates the difference in structure for adding a todo item between a basic scattered approach and the organized 'Actions' structure. The 'Actions' approach centralizes logic within the state management system.
```typescript
// ❌ Basic structure: logic scattered in component
const addTodo = () => {
if (!newTodoText.trim()) return;
const todos = state.getValues().todos;
state.setValue("todos", [
...todos,
{ id: Date.now(), text: newTodoText, completed: false },
]);
state.setValue("newTodoText", "");
};
// ✅ Actions structure: logic organized in one place
state.actions.addTodo();
```
--------------------------------
### useFormaState Basic Usage Example in TypeScript
Source: https://github.com/ehfuse/forma/blob/main/docs/en/examples/basic-example.md
Demonstrates the basic usage of the useFormaState hook for managing form state, including individual field subscriptions, checking field existence, getting field values, subscribing to global state changes, and performing actions like removing fields and resetting state. It utilizes React's useEffect for global subscriptions.
```typescript
import { useFormaState } from "forma";
import React from "react";
// Basic usage
function MyComponent() {
const state = useFormaState({
user: { name: "", email: "" },
settings: { theme: "light", notifications: true },
});
// Individual field subscription (re-renders only when that field changes)
const userName = state.useValue("user.name");
const theme = state.useValue("settings.theme");
// Using new API methods
const hasUserEmail = state.hasField("user.email");
const userEmailValue = state.getValue("user.email"); // Non-reactive
// Subscribe to global state changes
React.useEffect(() => {
const unsubscribe = state.subscribe((values) => {
console.log("Global state changed:", values);
});
return unsubscribe;
}, [state]);
return (
);
}
```
--------------------------------
### Dynamic State Management with Forma
Source: https://github.com/ehfuse/forma/blob/main/docs/en/examples/basic-example.md
Shows how to dynamically switch between different global states using Forma by updating the stateId. This is useful for managing distinct sets of data or configurations, such as user sessions.
```typescript
function DynamicStateManager() {
const [stateId, setStateId] = useState("session-1");
const state = useGlobalFormaState({
stateId,
initialValues: { data: {}, metadata: { created: Date.now() } },
});
const switchSession = (newId: string) => {
setStateId(newId); // Switch to different global state
};
return (
Current session: {stateId}
{/* Displays state of currently selected session */}
);
}
```
--------------------------------
### Basic Global State Usage with Forma
Source: https://github.com/ehfuse/forma/blob/main/docs/en/examples/basic-example.md
Demonstrates how to set up and use global state across multiple components using Forma's GlobalFormaProvider and useGlobalFormaState hooks. It shows how components can share and update the same state based on a stateId.
```typescript
import { useGlobalFormaState, GlobalFormaProvider } from "@ehfuse/forma";
// App.tsx
function App() {
return (
);
}
// User profile component
function UserProfile() {
const state = useGlobalFormaState({
stateId: "user-data",
initialValues: {
user: { name: "", email: "" },
preferences: { theme: "light", language: "en" },
},
});
const userName = state.useValue("user.name");
const userEmail = state.useValue("user.email");
return (
);
}
// User settings component (shares same state)
function UserSettings() {
const state = useGlobalFormaState({
stateId: "user-data", // Share state with same ID
// initialValues omitted - already created in UserProfile (ignored even if provided)
});
const theme = state.useValue("preferences.theme");
const language = state.useValue("preferences.language");
const userName = state.useValue("user.name"); // Value entered from other component
return (
User: {userName}
);
}
```
--------------------------------
### useFormaState Array State Management Example in TypeScript
Source: https://github.com/ehfuse/forma/blob/main/docs/en/examples/basic-example.md
Illustrates how to manage array state using the useFormaState hook. This example demonstrates subscribing to specific array elements, the array's length for add/remove operations, and updating individual items within the array. It highlights how changes to array content versus array structure trigger different subscription updates.
```typescript
// Array state management
function TodoList() {
const state = useFormaState({
todos: [
{ id: 1, text: "Learn React", completed: false },
{ id: 2, text: "Build app", completed: false },
],
});
// Subscribe to specific todo item
const firstTodo = state.useValue("todos.0.text");
// Subscribe only to array length (re-renders only when items are added/removed)
const todoCount = state.useValue("todos.length");
const addTodo = () => {
const todos = state.getValues().todos;
state.setValue("todos", [
...todos,
{ id: Date.now(), text: "New todo", completed: false },
]);
// When todos array changes, todos.length subscribers are automatically notified
};
const updateTodo = (index: number, newText: string) => {
// Only array content changes (length stays same) - todos.length subscribers are not notified
state.setValue(`todos.${index}.text`, newText);
};
const removeTodo = (index: number) => {
state.removeField(`todos.${index}`);
};
return (
First todo: {firstTodo}
Total todos: {todoCount}
);
}
```
--------------------------------
### Basic Form Setup: Redux Forms vs. Forma
Source: https://github.com/ehfuse/forma/blob/main/docs/en/migration.md
Compares the basic setup for creating a contact form using Redux Forms and Forma. Redux Forms requires explicit Field components, while Forma utilizes a `useForm` hook for a more streamlined approach.
```typescript
import { Field, reduxForm } from "redux-form";
let ContactForm = (props) => {
const { handleSubmit } = props;
return (
);
};
ContactForm = reduxForm({
form: "contact",
})(ContactForm);
```
```typescript
import { useForm } from "@ehfuse/forma";
function ContactForm() {
const form = useForm({
initialValues: { name: "", email: "" },
onSubmit: (values) => {
console.log(values);
},
});
return (
);
}
```
--------------------------------
### TanStack Form Initialization
Source: https://github.com/ehfuse/forma/blob/main/docs/en/library-comparison.md
An example of initializing a form using TanStack Form, a modern form library. It shows the basic setup with default values.
```typescript
// TanStack Form
const form = useForm({
defaultValues: {
name: ""
},
});
```
--------------------------------
### Efficient Array Length Subscription in TSX
Source: https://github.com/ehfuse/forma/blob/main/docs/en/examples/todoapp-example.md
Demonstrates smart subscription to array length for performance optimization. The 'Smart subscription' example efficiently updates a counter only when items are added or removed. The 'Inefficient' example shows how subscribing to the entire array and then accessing its length can lead to unnecessary re-renders on every todo change.
```tsx
// ✅ Smart subscription: update counter only on add/remove
const todoCount = state.useValue("todos.length");
// ❌ Inefficient: re-render on every todo change
const todoCount = state.useValue("todos").length;
```
--------------------------------
### Computed Values Example (TypeScript/JSX)
Source: https://github.com/ehfuse/forma/blob/main/docs/en/examples/todoapp-example.md
Shows how computed values like filtered todos and counts can be managed as getters within the 'Actions' structure, simplifying data retrieval and ensuring consistency across the application.
```typescript
// Manage filtering, counting, etc. logic as getters
const filteredTodos = state.actions.getFilteredTodos();
const completedCount = state.actions.getCompletedCount();
const remainingCount = state.actions.getRemainingCount();
```
--------------------------------
### Basic Form Management with useForm (TypeScript)
Source: https://github.com/ehfuse/forma/blob/main/docs/en/examples/basic-example.md
Demonstrates basic form management using the `useForm` hook. It initializes form values, defines an onSubmit handler, and includes an onValidate function. Individual field values can be subscribed to for performance optimization.
```typescript
const form = useForm({
initialValues: {
name: "",
email: "",
age: 0,
},
onSubmit: async (values) => {
await api.submitUser(values);
},
onValidate: async (values) => {
return values.email.includes("@");
},
});
// Individual field subscription (performance optimization)
const name = form.useFormValue("name");
const email = form.useFormValue("email");
```
--------------------------------
### Real-world Example: Bulk Checkbox Selection with setBatch
Source: https://github.com/ehfuse/forma/blob/main/docs/en/performance-guide.md
Demonstrates a practical use case for setBatch by updating the 'checked' status of 100 checkboxes simultaneously. This highlights the efficiency of batching updates for numerous similar fields.
```tsx
function selectAllCheckboxes() {
const updates: Record = {};
// Select 100 checkboxes at once
Array.from({ length: 100 }, (_, i) => {
updates[`items.${i}.checked`] = true;
});
state.setBatch(updates);
// → Individual setValue: immediate listener execution for each field
// → setBatch: collects all changes and executes listeners once at the end
}
```
--------------------------------
### Forma Error Handling Example
Source: https://github.com/ehfuse/forma/blob/main/docs/en/migration.md
Shows how error feedback is handled in Forma via the `onValidate` callback, compared to directly accessing error objects in older libraries.
```typescript
// Old way:
{
errors.name && Name is required;
}
```
```typescript
// New way:
// Validation feedback through onValidate callback
const form = useForm({
onValidate: (values) => {
if (!values.name) {
alert("Name is required"); // or set error state
return false;
}
return true;
},
});
```
--------------------------------
### Dependency Injection via Currying for Actions (JS)
Source: https://github.com/ehfuse/forma/blob/main/docs/en/examples/todoapp-example.md
An advanced pattern demonstrating how currying can be used to inject external dependencies like API functions, configuration, or mock data into Actions, enhancing flexibility and testability.
```JS
// Example: Auth Actions with API Dependencies (Conceptual)
const createAuthActions = (apiClient) => ({
login: async (username, password) => {
const user = await apiClient.post('/login', { username, password });
// ... other logic
return user;
},
// ... other auth actions
});
// Usage with injected API client
const authActions = createAuthActions(myApiClient);
// Usage in tests with mock API client
const mockApiClient = { post: jest.fn() };
const testAuthActions = createAuthActions(mockApiClient);
```
--------------------------------
### Nested Object Handling with useForm (TypeScript)
Source: https://github.com/ehfuse/forma/blob/main/docs/en/examples/basic-example.md
Illustrates how to manage nested objects within a form using the `useForm` hook. It shows how to initialize deeply nested values and access them using dot notation for field subscriptions.
```typescript
const form = useForm({
initialValues: {
user: {
profile: {
name: "",
settings: {
theme: "light",
},
},
},
},
});
// Access nested objects with dot notation
const name = form.useFormValue("user.profile.name");
const theme = form.useFormValue("user.profile.settings.theme");
```
--------------------------------
### Reusability of Actions in Multiple UI Elements (TSX)
Source: https://github.com/ehfuse/forma/blob/main/docs/en/examples/todoapp-example.md
Illustrates how a single action, like `addTodo`, can be invoked from various UI components, such as buttons and input fields, showcasing the reusability benefit of the Actions pattern.
```tsx
// Same action callable from multiple places
e.key === "Enter" && state.actions.addTodo()} />
```
--------------------------------
### Responsive Navigation with useBreakpoint Hook
Source: https://github.com/ehfuse/forma/blob/main/docs/en/examples/basic-example.md
Shows how to use the `useBreakpoint` hook from `@ehfuse/forma` to conditionally render different navigation elements based on screen size. It displays a horizontal navigation for larger screens (smUp) and a hamburger menu for smaller screens.
```typescript
import { useBreakpoint } from "@ehfuse/forma";
function ResponsiveNavigation() {
const { smUp } = useBreakpoint();
return (
{smUp ? (
// Desktop: Horizontal navigation
) : (
// Mobile: Hamburger menu
)}
);
}
```
--------------------------------
### Dynamic Import of Minified Forma Bundle
Source: https://github.com/ehfuse/forma/blob/main/docs/en/getting-started.md
Demonstrates dynamically importing the minified Forma bundle using `await import()`. This technique is ideal for code splitting, allowing the Forma library to be loaded on demand, improving initial page load performance.
```tsx
// Lazy load minified bundle
const { useForm } = await import("@ehfuse/forma/min");
```
--------------------------------
### Dynamic Component Sizing (ImageGallery Example)
Source: https://github.com/ehfuse/forma/blob/main/docs/en/API.md
Demonstrates how to dynamically size components based on screen breakpoints using the useBreakpoint hook. This approach allows for responsive UIs but should be used judiciously due to potential re-renders on window resize.
```APIDOC
## Dynamic Component Sizing
### Description
Dynamically sizes components based on screen breakpoints. Re-renders occur on window resize. Use only when necessary for performance, preferring CSS media queries when possible. Initial value is 0px during server-side rendering.
### Usage Example
```typescript
function ImageGallery() {
const { xs, sm, md, lg } = useBreakpoint();
const columns = xs ? 1 : sm ? 2 : md ? 3 : lg ? 4 : 5;
return (
{images.map((img) => (
))}
);
}
```
### Important Notes
* Re-renders occur on window resize.
* Use only when necessary for performance (prefer CSS media queries when possible).
* Initial value is 0px during server-side rendering.
### Related Links
* [Detailed Breakpoint Examples →](./examples.md#usebreakpoint-examples)
```
--------------------------------
### Product Page Layouts with useBreakpoint Hook
Source: https://github.com/ehfuse/forma/blob/main/docs/en/examples/basic-example.md
Demonstrates adapting a product page layout based on screen size using the `useBreakpoint` hook. It defines different rendering logic for extra-small (xs), small (sm), and medium-up (mdUp) breakpoints, allowing for mobile, tablet, and desktop views with varying component arrangements.
```typescript
import { useBreakpoint } from "@ehfuse/forma";
function ProductPage() {
const { xs, sm, mdUp } = useBreakpoint();
// Use different layouts based on screen size
if (xs) {
return (
);
}
if (sm) {
return (
);
}
// Desktop (md and up)
return (
);
}
```
--------------------------------
### Testing Forma Actions with Mocked Dependencies
Source: https://github.com/ehfuse/forma/blob/main/docs/en/examples/todoapp-example.md
Demonstrates how to leverage the currying pattern for effective unit testing of Forma actions. By injecting mock API functions, developers can isolate the logic within the actions and assert their behavior without making actual API calls.
```tsx
// Easily inject mock API in tests
const mockApi = {
login: jest.fn().mockResolvedValue({ success: true }),
logout: jest.fn().mockResolvedValue(undefined),
};
const form = useForm({
initialValues: testData,
actions: {
login: AuthActions.login(mockApi.login, mockSetDialog),
logout: AuthActions.logout(mockApi.logout),
},
});
// Execute Actions and verify mock was called
await form.actions.login();
expect(mockApi.login).toHaveBeenCalledWith("user", "pass");
```
--------------------------------
### Form Wizard using Ehfust Forma with Multiple Steps
Source: https://github.com/ehfuse/forma/blob/main/docs/en/useGlobalForm-guide.md
This example demonstrates a multi-step form wizard for user registration using Ehfust Forma. It shows how to manage form state across different components (`Step1`, `Step2`, `ReviewStep`) using a shared `formId` and handles navigation and submission.
```typescript
// Example of how the steps might be orchestrated
function RegistrationWizard() {
const [step, setStep] = useState(0);
const handleNext = () => setStep(step + 1);
const handlePrev = () => setStep(step - 1);
const renderStep = () => {
switch (step) {
case 0:
return ;
case 1:
return ;
case 2:
return ;
default:
return ;
}
};
return (
User Registration
{renderStep()}
);
}
```
--------------------------------
### Migration from Formik to Forma
Source: https://github.com/ehfuse/forma/blob/main/docs/en/library-comparison.md
Illustrates migrating from Formik to Forma. The example demonstrates how to initialize form values and handle input changes using Forma's `useForm` and `handleFormChange`, compared to Formik's `values` and `setFieldValue`.
```typescript
// Before (Formik)
{({
values,
setFieldValue
}) => (
setFieldValue("name", e.target.value)}
/>
)}
;
// After (Forma)
const form = useForm({ initialValues: { name: "" } });
const name = form.useFormValue("name");
return ;
```
--------------------------------
### FormaJS 'watch' Usage Example with Async Operations (TypeScript)
Source: https://github.com/ehfuse/forma/blob/main/docs/API-en.md
This example showcases an asynchronous usage of the FormaJS `watch` feature, specifically synchronizing user data based on a 'logined' state. It demonstrates how to use `async/await` within a watch handler to perform operations like starting and stopping data synchronization intervals. The example also shows how to manage interval IDs within the state using `setValue` and `getValue` from the `ActionContext`.
```typescript
// Automatically sync data based on login state
const appState = useGlobalFormaState({
stateId: "app",
initialValues: { logined: false, syncInterval: null },
watch: {
logined: async (context, value) => {
if (value) {
// Start data synchronization on login
const intervalId = setInterval(async () => {
await syncUserData();
}, 5000);
context.setValue("syncInterval", intervalId);
} else {
// Stop synchronization on logout
const intervalId = context.getValue("syncInterval");
if (intervalId) {
clearInterval(intervalId);
context.setValue("syncInterval", null);
}
}
},
},
});
```
--------------------------------
### Global State Management with Forma
Source: https://github.com/ehfuse/forma/blob/main/docs/en/migration.md
Illustrates Forma's built-in global state management capabilities, contrasting it with the need for external state management solutions in previous setups.
```typescript
// Need external state management for form sharing
const [formData, setFormData] = useState({});
```
```typescript
// Built-in global form state
const form = useGlobalForm({ formId: "shared-form" });
```
--------------------------------
### Declare useFormaState with Basic Usage
Source: https://github.com/ehfuse/forma/blob/main/docs/en/getting-started.md
Shows basic usage of the `useFormaState` hook by providing initial values for nested objects. This is a straightforward way to initialize state with predefined structures.
```tsx
import { useFormaState } from "@/forma";
// 2. Basic usage - with initial values
const state = useFormaState({
user: { name: "", email: "" },
settings: { theme: "light" },
});
```
--------------------------------
### Declare useFormaState with Options
Source: https://github.com/ehfuse/forma/blob/main/docs/en/getting-started.md
Demonstrates using `useFormaState` with an options object, including an `onChange` callback to log state changes and `deepEquals` set to `true` for deep comparison of state updates. This allows for more control over state update behavior and event handling.
```tsx
import { useFormaState } from "@/forma";
// 4. Use with options
const stateWithOptions = useFormaState(
{
data: {},
},
{
onChange: (values) => console.log("State changed:", values),
deepEquals: true, // Enable deep equality check
}
);
```
--------------------------------
### Form Hooks: useForm, useGlobalForm, and useFormaState in React
Source: https://github.com/ehfuse/forma/blob/main/docs/en/performance-guide.md
Demonstrates the distinct use cases for useForm (single component), useGlobalForm (multi-component/page forms), and useFormaState (general state management). Includes examples of basic state access and optimization for complex data structures with useFormaState.
```tsx
import { useForm, useGlobalForm, useFormaState } from '@forma/react';
// ✅ Single component form → useForm
function ContactForm() {
const form = useForm({
initialValues: { name: "", email: "" },
});
}
// ✅ Multi-component/page form → useGlobalForm
function MultiStepForm() {
const form = useGlobalForm({
formId: "user-registration",
});
}
// ✅ General state management (not form) → useFormaState
function UserDashboard() {
const state = useFormaState({
user: { name: "John Doe", status: "online" },
theme: "dark",
});
const userName = state.useValue("user.name");
const theme = state.useValue("theme");
return (
Hello, {userName}! Theme: {theme}
);
}
// ✅ Complex array/object state → useFormaState (individual subscriptions)
function TodoManager() {
const state = useFormaState({
todos: [
{ id: 1, text: "Learn React", completed: false },
{ id: 2, text: "Try Forma", completed: true },
],
filter: "all",
});
// ❌ Whole array subscription - re-renders on all todo changes
// const todos = state.useValue("todos");
// ✅ Individual todo item subscription (performance optimization)
const firstTodo = state.useValue("todos.0.text");
const secondCompleted = state.useValue("todos.1.completed");
return (
First: {firstTodo}
);
}
```
--------------------------------
### Declare useFormaState with Dynamic Object
Source: https://github.com/ehfuse/forma/blob/main/docs/en/getting-started.md
Illustrates initializing `useFormaState` with an empty object and specifying a generic type `Record`. This allows for dynamic addition of properties to the state object after its initial declaration.
```tsx
import { useFormaState } from "@/forma";
// 3. Start with an empty object (dynamic field addition)
const dynamicState = useFormaState>();
```
--------------------------------
### Import Forma Core Hooks (Standard)
Source: https://github.com/ehfuse/forma/blob/main/docs/en/getting-started.md
Imports the main hooks provided by the Forma library for form management and state. These are essential for building forms and managing their state within React applications.
```tsx
import {
useForm,
useFormaState,
GlobalFormProvider,
useGlobalForm,
} from "@ehfuse/forma";
```
--------------------------------
### Automatic Memory Cleanup with Forma
Source: https://github.com/ehfuse/forma/blob/main/docs/en/examples/basic-example.md
Explains how Forma automatically cleans up global state when it's no longer referenced by any components. The `autoCleanup: true` option ensures that memory is managed efficiently, preventing leaks.
```typescript
// Component A mounts → reference count: 1
function ComponentA() {
const state = useGlobalFormaState({
stateId: "shared",
autoCleanup: true,
});
return
{state.useValue("data")}
;
}
// Component B mounts → reference count: 2
function ComponentB() {
const state = useGlobalFormaState({
stateId: "shared", // Same ID
autoCleanup: true,
});
return
{state.useValue("data")}
;
}
// Component A unmounts → reference count: 1 (state preserved)
// Component B unmounts → reference count: 0 → 🗑️ Automatic cleanup!
```
--------------------------------
### Global Form Registration and Access with useRegisterGlobalForm
Source: https://github.com/ehfuse/forma/blob/main/docs/en/examples/basic-example.md
Demonstrates how to register a local form instance as a global form using `useRegisterGlobalForm` and access it from other components using `useGlobalForm`. This facilitates state management and data sharing between different parts of the application, enabling components to interact with a shared form state.
```typescript
import { useForm, useRegisterGlobalForm } from "@ehfuse/forma";
function MyComponent() {
// Create local form
const form = useForm<{ name: string; email: string }>({
initialValues: { name: "", email: "" },
onSubmit: async (values) => console.log(values),
});
// Register as global form
useRegisterGlobalForm("shared-form", form);
return (
);
}
// Access from another component
function AnotherComponent() {
const form = useGlobalForm<{ name: string; email: string }>({
formId: "shared-form",
});
return
Name: {form.useFormValue("name")}
;
}
```
--------------------------------
### Automatic Memory Cleanup: Enabled by Default
Source: https://github.com/ehfuse/forma/blob/main/docs/en/examples/basic-example.md
Illustrates the `autoCleanup` option in `useGlobalForm`. When `autoCleanup` is true (the default), the form state is automatically managed and cleaned up, which is suitable for transient forms like multi-step wizards.
```typescript
// Automatic cleanup in multi-step forms
function Step1() {
const form = useGlobalForm({
formId: "wizard-form",
autoCleanup: true, // Default - automatic cleanup
});
return ;
}
function Step2() {
const form = useGlobalForm({
formId: "wizard-form", // Share same form
autoCleanup: true,
});
return ;
}
```
--------------------------------
### Multi-Step User Registration Form Example
Source: https://github.com/ehfuse/forma/blob/main/docs/en/useGlobalForm-guide.md
A foundational component for a multi-step user registration wizard. This component (`PersonalInfoStep`) uses `useGlobalForm` to manage form state across different steps of the registration process.
```tsx
// Step 1: Personal Information
function PersonalInfoStep({ onNext }) {
const form = useGlobalForm({
formId: "registration-wizard",
initialValues: {
personal: { firstName: "", lastName: "", email: "" },
address: { street: "", city: "", zipCode: "" },
preferences: { newsletter: false, theme: "light" }
}
});
const firstName = form.useFormValue("personal.firstName");
const lastName = form.useFormValue("personal.lastName");
const email = form.useFormValue("personal.email");
// ... rest of the component logic for input fields and navigation buttons ...
}
```
--------------------------------
### Forma Field Binding Example
Source: https://github.com/ehfuse/forma/blob/main/docs/en/migration.md
Demonstrates the difference in field binding syntax between older libraries like React Hook Form and Forma. Forma uses `name`, `value`, and `onChange` attributes directly.
```typescript
// Old way (React Hook Form):
```
```typescript
// New way (Forma):
```
--------------------------------
### TypeScript Migration: Formik to Forma
Source: https://github.com/ehfuse/forma/blob/main/docs/en/API.md
Provides a TypeScript example of migrating from Formik to Forma. It compares the initialization and value access methods, showing how to set initial values and retrieve form field values in both Formik and Forma.
```typescript
// Formik
const formik = useFormik({ initialValues: { name: "" } });
const name = formik.values.name;
// Forma
const form = useForm({ initialValues: { name: "" } });
const name = form.useFormValue("name");
```
--------------------------------
### Forma: Resetting and Updating Form Initial Values
Source: https://github.com/ehfuse/forma/blob/main/docs/en/getting-started.md
Shows how to reset form values using `setInitialFormValues`, update a single field with `setFormValue`, and update multiple fields simultaneously with `setValues` after a form has been initialized.
```tsx
import { useGlobalForm } from "forma";
interface UserForm {
name: string;
email: string;
}
const form = useGlobalForm({
formId: "user-registration",
initialValues: { name: "", email: "" },
});
// Reset form values later
function resetFormValues() {
form.setInitialFormValues({
name: "john",
email: "john@example.com",
});
}
// Update a specific field
function updateName() {
form.setFormValue("name", "jane");
}
// Update multiple fields at once
function updateMultiple() {
form.setValues({
name: "bob",
email: "bob@example.com",
});
}
```
--------------------------------
### Basic Form Usage: React Hook Form vs Forma
Source: https://github.com/ehfuse/forma/blob/main/docs/en/migration.md
Compares the basic form setup between React Hook Form and Forma. Shows how to initialize forms, handle input changes, and submit data. Forma uses a single `useForm` hook with explicit `onSubmit` and `onValidate` callbacks, while React Hook Form uses `handleSubmit` and `register`.
```typescript
import { useForm } from "react-hook-form";
function MyForm() {
const {
register,
handleSubmit,
watch,
formState: { errors },
} = useForm({
defaultValues: { name: "", email: "" },
});
const name = watch("name");
const onSubmit = (data) => {
console.log(data);
};
return (
);
}
```
```typescript
import { useForm } from "@ehfuse/forma";
function MyForm() {
const form = useForm({
initialValues: { name: "", email: "" },
onSubmit: (values) => {
console.log(values);
},
onValidate: (values) => {
return values.name && values.email; // Simple validation
},
});
const name = form.useFormValue("name");
return (
);
}
```
--------------------------------
### Conditional Component Rendering with useBreakpoint
Source: https://github.com/ehfuse/forma/blob/main/docs/en/examples/basic-example.md
Conditionally renders components like video controls, playlists, and recommended videos based on screen size breakpoints (smUp, mdUp). This enables showing or hiding specific UI elements to optimize user experience on different devices. For example, basic controls might appear on small screens, while playlists and recommendations appear on larger ones.
```typescript
import { useBreakpoint } from "@ehfuse/forma";
function VideoPlayer() {
const { smUp, mdUp } = useBreakpoint();
return (
{/* Basic controls only on small screens */}
{smUp && }
{/* Playlist from medium size */}
{mdUp && }
{/* Recommended videos on large screens */}
{mdUp && }
);
}
```
--------------------------------
### Forma MUI: Integrating DatePicker Component
Source: https://github.com/ehfuse/forma/blob/main/docs/en/getting-started.md
Demonstrates how to integrate Material-UI's `DatePicker` component with Forma. It shows setting initial values for a 'birthDate' field and handling date changes using `useFormValue` and `handleDatePickerChange`.
```tsx
import { DatePicker } from "@mui/x-date-pickers/DatePicker";
import { useForm } from "forma";
interface BirthDateForm {
birthDate: string;
}
function FormWithDate() {
const form = useForm({
initialValues: { birthDate: "" },
});
const birthDate = form.useFormValue("birthDate");
return (
);
}
```