UNPKG

use-inertia-form

Version:

Extra functionality for Inertia.js useForm hook

214 lines (162 loc) 6.39 kB
# Introduction A hook for using forms with Inertia.js, meant to be used as a direct replacement of Inertia's `useForm` hook. It address two issues with the original: [the bug preventing the `transform` method from running on submit](https://github.com/inertiajs/inertia/issues/1131), and [the lack of support for nested form data](https://github.com/inertiajs/inertia/discussions/1174). This was developed alongside a Rails project, so the form handling ethos follows Rails conventions, however, effort was taken to make it as agnostic as possible and it should be useable in a Laravel application as well. ## References ### Wiki I've chosen to keep the main readme clean and only provide basic examples to help get started. [More in depth documentation is available on the Wiki section of this repo](https://github.com/aviemet/useInertiaForm/wiki) ### Codesandbox [Here is a codesandbox with usage examples for all hooks and components](https://codesandbox.io/s/useinertiaform-examples-0so45f) If you encounter a bug, please try to recreate it with a fork of the codesandbox below and submit it with the issue. I don't have much time to address issues, so seeing an actual recreation can really help me identify if it's something I should look into. ## Quickstart Below are basic usage examples of the exported members of this project ### [useInertiaForm](https://github.com/aviemet/useInertiaForm/wiki/1.-useInertiaForm) Drop in replacement for Inertia.js' `useForm`, with support for nested data ```typescript const { data, setData, getData, unsetData, errors, setError, getError } = useInertiaForm({ user: { firstName: 'Finn' lastName: undefined } }) getData('user.firstName') setData('user.lastName', 'Human') ``` ### [Form component](https://github.com/aviemet/useInertiaForm/wiki/2.-Form-component) Create a custom form component for your project ```typescript import { Form as InertiaForm, type FormProps, type NestedObject } from 'use-inertia-form' const Form = <TForm extends NestedObject>( { children, railsAttributes = true, className, ...props }: FormProps<TForm>, ) => { return ( <div className="form-wrapper"> <InertiaForm className={ `form ${className}` } railsAttributes={ railsAttributes } { ...props } > { children } </InertiaForm> </div> ) } ``` ### [useInertiaInput](https://github.com/aviemet/useInertiaForm/wiki/2.1-useInertiaInput) Create custom inputs for your project ```typescript import { useInertiaInput } from 'use-inertia-form' const TextInput = ({ name, model, label }) => { const { inputName, inputId, value, setValue, error } = useInertiaInput({ name, model }) return ( <label for={ inputId }>{ label }</label> <input type='text' id={ inputId } name={ inputName } value={ value } onChange={ e => setValue(e.target.value) } > { error && <div className="error">{ error }</div> } ) } ``` ### [NestedFields](https://github.com/aviemet/useInertiaForm/wiki/2.2-NestedFields) Helper component for visually specifying nested data lookup context ```typescript import { NestedFields } from 'use-inertia-form' import { Form, TextInput } from 'my/project/components' const user = { firstName: 'Finn', preferences: { princess: 'Bubblegum', sword: 'Scarlet' } } const EditUserForm = () => { return ( <Form model="user" data={ { user } } to={ `users/${user.id}` } method="patch"> <TextInput name="firstName" label="First Name" /> {/* Create a nested model context of `form.data.user.preferences` */} <NestedFields model="preferences"> {/* This input would sync to `form.data.user.preferences.princess` */} <TextInput name="princess" label="Favorite Princess" /> {/* And this would sync to `form.data.user.preferences.sword` */} <TextInput name="sword" label="Favorite Sword" /> </NestedFields> </Form> ) } ``` ### [useDynamicInputs](https://github.com/aviemet/useInertiaForm/wiki/2.3-useDynamicInputs) Build a custom component for handling arrays in nested data object ```typescript import { useDynamicInputs, NestedFields } from 'use-inertia-form' const DynamicInputs = ({ children, model, label, emptyData }) => { const { addInput, removeInput, paths } = useDynamicInputs({ model, emptyData }) return ( <> <div style={ { display: 'flex' } }> <label style={ { flex: 1 } }>{ label }</label> <button onClick={ addInput }>+</button> </div> { paths.map((path, i) => ( <NestedFields key={ i } model={ path }> <div style={ { display: 'flex' } }> <div>{ children }</div> <button onClick={ onClick: () => removeInput(i) }>-</button> </div> </NestedFields> )) } </> ) } ``` ### [Submit](https://github.com/aviemet/useInertiaForm/wiki/2.4-Submit-component) Not necessary to use, any submit button in the `<Form>` will cause the form to submit. This component just has a few standard features already built in. ### Putting it all together You can now use those components together to build forms with nested data support. ```typescript import { NestedFields, Submit } from 'use-inertia-form' import { Form, TextInput, DynamicInputs } from 'my/project/components' // This probably comes from the server const user = { user: { id: 1, firstName: "Jake", email: "jake@thetreehouse.ooo", home: { name: "The Treehouse", location: "Ooo", }, friends: [ { name: "Finn", email: 'finn@thetreehouse.ooo' }, { name: "BMO", email: 'bmo@thetreehouse.ooo' }, ] } } const EditUserForm = ({ user }) => { return ( <Form model="user" data={ { user } } to={ `users/${user.id}` } method="patch" filter="user.id" > <TextInput name="firstName" label="First Name" /> <TextInput name="email" label="Email" /> <NestedFields model="home"> <TextInput name="name" label="Home Name"> <TextInput name="location" label="Home Location"> </NestedFields> <DynamicInputs model="friends" emptyData={ {name: ''} } label="Friends"> <TextInput name="name" label="Name" /> <TextInput name="email" label="Email" /> </DynamicInputs> <Submit>Update User</Submit> </Form> ) } ```