form icon indicating copy to clipboard operation
form copied to clipboard

[WIP] Shared Component Library Docs

Open crutchcorn opened this issue 1 year ago • 2 comments

This PR:

  • [x] Adds an example of moving TanStack Form into your own shared component library
  • [ ] Adds the docs to explain what the example is doing

crutchcorn avatar Jul 08 '24 19:07 crutchcorn

☁️ Nx Cloud Report

We didn't find any information for the current pull request with the commit 2adadbf80b3c82802a83a53bba6fec2d09f54e97. Please verify you are running the latest version of the NxCloud runner.

Check the Nx Cloud Source Control Integration documentation for more information.

Alternatively, you can contact us at [email protected].


Sent with 💌 from NxCloud.

nx-cloud[bot] avatar Jul 08 '24 19:07 nx-cloud[bot]

Review PR in StackBlitz Codeflow Run & review this pull request in StackBlitz Codeflow.

commit: bb26093

@tanstack/angular-form

npm i https://pkg.pr.new/@tanstack/angular-form@825
@tanstack/form-core

npm i https://pkg.pr.new/@tanstack/form-core@825
@tanstack/lit-form

npm i https://pkg.pr.new/@tanstack/lit-form@825
@tanstack/react-form

npm i https://pkg.pr.new/@tanstack/react-form@825
@tanstack/solid-form

npm i https://pkg.pr.new/@tanstack/solid-form@825
@tanstack/valibot-form-adapter

npm i https://pkg.pr.new/@tanstack/valibot-form-adapter@825
@tanstack/vue-form

npm i https://pkg.pr.new/@tanstack/vue-form@825
@tanstack/yup-form-adapter

npm i https://pkg.pr.new/@tanstack/yup-form-adapter@825
@tanstack/zod-form-adapter

npm i https://pkg.pr.new/@tanstack/zod-form-adapter@825

templates

pkg-pr-new[bot] avatar Jul 08 '24 19:07 pkg-pr-new[bot]

One thing I am running into with this branch, field.state.value seems to always be string. If you have something else as the value you run into a world of pain. What is the best course of action for field values that are not string?

izakfilmalter avatar Nov 26 '24 20:11 izakfilmalter

One thing I am running into with this branch, field.state.value seems to always be string. If you have something else as the value you run into a world of pain. What is the best course of action for field values that are not string?

How are you defining your component?

function TextInputField<
  TFormData extends unknown,
  TName extends DeepKeyValueName<TFormData, string>,
>

Here you can change string to your data type

Balastrong avatar Nov 26 '24 20:11 Balastrong

CleanShot 2024-11-26 at 17 31 09

izakfilmalter avatar Nov 26 '24 22:11 izakfilmalter

Ended up doing this, which is stupid, but works.

type ExpirationSelectFieldProps<
  Key extends string,
  TFormData extends { [K in Key]: ExpirationItem | null },
  TName extends DeepKeyValueName<TFormData, ExpirationItem>,
> = Omit<FieldOptions<TFormData, TName>, 'name'> & {
  name: Key
  form: ReactFormApi<TFormData, any> & {
    state: FormState<TFormData>
  }
  selectProps?: Omit<ComponentProps<typeof Select>, 'onChange' | 'value'> & {
    label?: string
    placeholder?: string
    inputWrapperClassName?: string
  }
  symbol: string
}

export function ExpirationSelectField<
  Key extends string,
  TFormData extends { [K in Key]: ExpirationItem | null },
  TName extends DeepKeyValueName<TFormData, ExpirationItem>,
>(props: ExpirationSelectFieldProps<Key, TFormData, TName>) { ... } 

izakfilmalter avatar Nov 27 '24 11:11 izakfilmalter

I was also working on integrating it more smoothly, i.e. abstracting the boiler plate and allowing to make custom components of it. This is my first POC, works for my side project now. Any feedback / suggestions / improvements are always welcome.

Usage:

// app/SomeForm.tsx
import { FieldInput, Form } from "../lib/form/Form"
export const SomeForm = () => {

    const form = useForm({
        defaultValues: {
            name: '',
            id: '',
        }
    })

    return (
        <Form onSubmit={form.handleSubmit}>
            <div className="flex flex-col space-y-2">
                <FieldInput formT={form} name="name" placeholder="Name" />
                <FieldInput formT={form} name="id" placeholder="ID" />
                <button type="submit">Add</button>
            </div>
        </Form>
    )
}

Abstract form components created to reduce boiler plate and allow for custom custom form components with @tanstack/form:

// lib/form/Form.tsx
import { DeepKeys, FieldApi, useForm } from '@tanstack/react-form'

export const Form: React.FC<React.FormHTMLAttributes<HTMLFormElement>> = ({
    children,
    ...props
}) => {
    return (
        <form
            {...props}
            onSubmit={(e) => {
                e.preventDefault()
                e.stopPropagation()
                if (props.onSubmit) {
                    props.onSubmit(e)
                }
            }}
        >
            {children}
        </form>
    )
}

export const FieldInfo = ({ field }: { field: FieldApi<any, any, any, any> }) => {
    return (
        <>
            {field.state.meta.isTouched && field.state.meta.errors.length ? (
                <em className=" text-red-500 text-xs">{field.state.meta.errors.join(',')}</em>
            ) : null}
            {field.state.meta.isValidating ? 'Validating...' : null}
        </>
    )
}

export const FieldInput = <TFormData, TNames extends DeepKeys<TFormData>>({
    formT,
    name,
    ...props
}: {
    formT: ReturnType<typeof useForm<TFormData, any>>
    name: TNames
} & React.InputHTMLAttributes<HTMLInputElement>) => (
    <formT.Field<TNames, any, any> name={name}>
        {(field) => (
            <>
                <input
                    value={field.state.value}
                    onChange={(e) => field.handleChange(e.target.value)}
                    onBlur={field.handleBlur}
                    className="w-full rounded-md border border-gray-300 px-4 py-2 transition duration-200 ease-in-out focus:border-transparent focus:outline-none focus:ring-2 focus:ring-blue-500"
                    {...props}
                />
                <FieldInfo field={field} />
            </>
        )}
    </formT.Field>
)

This works so far, but I might want to look into your solution @crutchcorn, with DeepKeyValueName, what's still going wrong (as it seems it's not merged)?

Meess avatar Dec 15 '24 14:12 Meess

There's an issue with DeepKeyValueName when a nested object is nullable:

  interface Fields {
    nested: {
      name: string
      labels: string[]
    } | null
  }

  const form = useForm<Fields>({
    defaultValues: {
      nested: { name: '', labels: [] },
    },
  })

  return <TextInputField form={form} name="nested.name" />
Type '"nested.name"' is not assignable to type '`nested.labels[${number}]`'.

RazerM avatar Jan 08 '25 12:01 RazerM

@RazerM can you open a different GH issue for the failing type error you're running into there?

crutchcorn avatar Jan 09 '25 08:01 crutchcorn

@crutchcorn it's unique to DeepKeyValueName added in this PR, <form.Field name={...} /> doesn't have the issue.

RazerM avatar Jan 09 '25 08:01 RazerM

i've been adapting this branch into my project, and it's been working really well so far. one thing i've noticed: adding an array field to my form triggers a typescript error:

Type instantiation is excessively deep and possibly infinite

beeirl avatar Feb 12 '25 19:02 beeirl

i've been adapting this branch into my project, and it's been working really well so far. one thing i've noticed: adding an array field to my form triggers a typescript error:

Type instantiation is excessively deep and possibly infinite

Did you use vs code? If so, I think vs code fails to infer the related types. Just pass a generic 'any' type to resolve that issue. image

epenflow avatar Feb 14 '25 10:02 epenflow

@epenflow yeah, i'm on cursor. had the same thought that my ide could be causing this, so i need to test with a different one to verify. i meant to delete my comment here since i'm using this branch, which i originally thought was related, but it's not. i don't think your fix will work in my case.

beeirl avatar Feb 14 '25 10:02 beeirl

Hey all! From the experimentation with this PR from many community contributors, it's become clear that this particular implantation was not the right direction for us.

To remedy this, we've introduced an alternative API via a new PR here: https://github.com/TanStack/form/pull/1179

This new API should have either substantially fewer or none of the same problems of typings as this one; all while allowing for more flexibility.

Please review #1179 and let me know your thoughts there - thanks!

crutchcorn avatar Feb 25 '25 08:02 crutchcorn