mirror of
https://github.com/LukeHagar/form.git
synced 2025-12-10 04:19:54 +00:00
fix: form.Provider
This commit is contained in:
@@ -1,6 +1,6 @@
|
|||||||
import React from "react";
|
import React from "react";
|
||||||
import ReactDOM from "react-dom/client";
|
import ReactDOM from "react-dom/client";
|
||||||
import { FieldApi, createFormFactory, useField } from "@tanstack/react-form";
|
import { FieldApi, createFormFactory } from "@tanstack/react-form";
|
||||||
|
|
||||||
type Person = {
|
type Person = {
|
||||||
firstName: string;
|
firstName: string;
|
||||||
@@ -41,137 +41,148 @@ export default function App() {
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const [count, setCount] = React.useState(0);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
|
<button onClick={() => setCount((prev) => prev + 1)}>{count}</button>
|
||||||
<h1>Simple Form Example</h1>
|
<h1>Simple Form Example</h1>
|
||||||
{/* A pre-bound form component */}
|
{/* A pre-bound form component */}
|
||||||
<form {...form.getFormProps()}>
|
<form.Provider>
|
||||||
<div>
|
<form {...form.getFormProps()}>
|
||||||
{/* A type-safe and pre-bound field component*/}
|
<div>
|
||||||
<form.Field
|
{/* A type-safe and pre-bound field component*/}
|
||||||
name="firstName"
|
<form.Field
|
||||||
validate={(value) => !value && "A first name is required"}
|
name="firstName"
|
||||||
validateAsyncOn="change"
|
validateOn="change"
|
||||||
validateAsyncDebounceMs={500}
|
validate={(value) => !value && "A first name is required"}
|
||||||
validateAsync={async (value) => {
|
validateAsyncOn="change"
|
||||||
await new Promise((resolve) => setTimeout(resolve, 1000));
|
validateAsyncDebounceMs={500}
|
||||||
return (
|
validateAsync={async (value) => {
|
||||||
value.includes("error") && 'No "error" allowed in first name'
|
await new Promise((resolve) => setTimeout(resolve, 1000));
|
||||||
);
|
return (
|
||||||
}}
|
value.includes("error") && 'No "error" allowed in first name'
|
||||||
children={(field) => (
|
);
|
||||||
// Avoid hasty abstractions. Render props are great!
|
}}
|
||||||
<>
|
children={(field) => {
|
||||||
<input {...field.getInputProps()} />
|
// Avoid hasty abstractions. Render props are great!
|
||||||
<FieldInfo field={field} />
|
return (
|
||||||
</>
|
<>
|
||||||
)}
|
<input placeholder="uncontrolled" />
|
||||||
/>
|
<input {...field.getInputProps()} />
|
||||||
</div>
|
<FieldInfo field={field} />
|
||||||
<div>
|
</>
|
||||||
<form.Field
|
);
|
||||||
name="lastName"
|
}}
|
||||||
children={(field) => (
|
/>
|
||||||
<>
|
</div>
|
||||||
<input {...field.getInputProps()} />
|
<div>
|
||||||
<FieldInfo field={field} />
|
<form.Field
|
||||||
</>
|
name="lastName"
|
||||||
)}
|
children={(field) => (
|
||||||
/>
|
<>
|
||||||
</div>
|
<input {...field.getInputProps()} />
|
||||||
<div>
|
<FieldInfo field={field} />
|
||||||
<form.Field
|
</>
|
||||||
name="hobbies"
|
)}
|
||||||
mode="array"
|
/>
|
||||||
children={(hobbiesField) => (
|
</div>
|
||||||
<div>
|
<div>
|
||||||
Hobbies
|
<form.Field
|
||||||
<div
|
name="hobbies"
|
||||||
style={{
|
mode="array"
|
||||||
paddingLeft: "1rem",
|
children={(hobbiesField) => (
|
||||||
display: "flex",
|
<div>
|
||||||
flexDirection: "column",
|
Hobbies
|
||||||
gap: "1rem",
|
<div
|
||||||
}}
|
style={{
|
||||||
>
|
paddingLeft: "1rem",
|
||||||
{!hobbiesField.state.value.length
|
display: "flex",
|
||||||
? "No hobbies found."
|
flexDirection: "column",
|
||||||
: hobbiesField.state.value.map((_, i) => (
|
gap: "1rem",
|
||||||
<div
|
}}
|
||||||
key={i}
|
>
|
||||||
style={{
|
{!hobbiesField.state.value.length
|
||||||
borderLeft: "2px solid gray",
|
? "No hobbies found."
|
||||||
paddingLeft: ".5rem",
|
: hobbiesField.state.value.map((_, i) => (
|
||||||
}}
|
<div
|
||||||
>
|
key={i}
|
||||||
<hobbiesField.Field
|
style={{
|
||||||
index={i}
|
borderLeft: "2px solid gray",
|
||||||
name="name"
|
paddingLeft: ".5rem",
|
||||||
children={(field) => {
|
|
||||||
return (
|
|
||||||
<div>
|
|
||||||
<label htmlFor={field.name}>Name:</label>
|
|
||||||
<input
|
|
||||||
name={field.name}
|
|
||||||
{...field.getInputProps()}
|
|
||||||
/>
|
|
||||||
<button
|
|
||||||
type="button"
|
|
||||||
onClick={() => hobbiesField.removeValue(i)}
|
|
||||||
>
|
|
||||||
X
|
|
||||||
</button>
|
|
||||||
<FieldInfo field={field} />
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}}
|
}}
|
||||||
/>
|
>
|
||||||
<hobbiesField.Field
|
<hobbiesField.Field
|
||||||
index={i}
|
index={i}
|
||||||
name="description"
|
name="name"
|
||||||
children={(field) => {
|
children={(field) => {
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<label htmlFor={field.name}>
|
<label htmlFor={field.name}>Name:</label>
|
||||||
Description:
|
<input
|
||||||
</label>
|
name={field.name}
|
||||||
<input
|
{...field.getInputProps()}
|
||||||
name={field.name}
|
/>
|
||||||
{...field.getInputProps()}
|
<button
|
||||||
/>
|
type="button"
|
||||||
<FieldInfo field={field} />
|
onClick={() =>
|
||||||
</div>
|
hobbiesField.removeValue(i)
|
||||||
);
|
}
|
||||||
}}
|
>
|
||||||
/>
|
X
|
||||||
</div>
|
</button>
|
||||||
))}
|
<FieldInfo field={field} />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<hobbiesField.Field
|
||||||
|
index={i}
|
||||||
|
name="description"
|
||||||
|
children={(field) => {
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<label htmlFor={field.name}>
|
||||||
|
Description:
|
||||||
|
</label>
|
||||||
|
<input
|
||||||
|
name={field.name}
|
||||||
|
{...field.getInputProps()}
|
||||||
|
/>
|
||||||
|
<FieldInfo field={field} />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
onClick={() =>
|
||||||
|
hobbiesField.pushValue({
|
||||||
|
name: "",
|
||||||
|
description: "",
|
||||||
|
yearsOfExperience: 0,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
>
|
||||||
|
Add hobby
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<button
|
)}
|
||||||
type="button"
|
/>
|
||||||
onClick={() =>
|
</div>
|
||||||
hobbiesField.pushValue({
|
<form.Subscribe
|
||||||
name: "",
|
selector={(state) => [state.canSubmit, state.isSubmitting]}
|
||||||
description: "",
|
children={([canSubmit, isSubmitting]) => (
|
||||||
yearsOfExperience: 0,
|
<button type="submit" disabled={!canSubmit}>
|
||||||
})
|
{isSubmitting ? "..." : "Submit"}
|
||||||
}
|
</button>
|
||||||
>
|
|
||||||
Add hobby
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
</div>
|
</form>
|
||||||
<form.Subscribe
|
</form.Provider>
|
||||||
selector={(state) => [state.canSubmit, state.isSubmitting]}
|
|
||||||
children={([canSubmit, isSubmitting]) => (
|
|
||||||
<button type="submit" disabled={!canSubmit}>
|
|
||||||
{isSubmitting ? "..." : "Submit"}
|
|
||||||
</button>
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
</form.Form>
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -129,6 +129,7 @@ export function Field<TData, TFormData>({
|
|||||||
children: (fieldApi: FieldApi<TData, TFormData>) => any
|
children: (fieldApi: FieldApi<TData, TFormData>) => any
|
||||||
} & UseFieldOptions<TData, TFormData>) {
|
} & UseFieldOptions<TData, TFormData>) {
|
||||||
const fieldApi = useField(fieldOptions as any)
|
const fieldApi = useField(fieldOptions as any)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<formContext.Provider
|
<formContext.Provider
|
||||||
value={{ formApi: fieldApi.form, parentFieldName: fieldApi.name }}
|
value={{ formApi: fieldApi.form, parentFieldName: fieldApi.name }}
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ declare module '@tanstack/form-core' {
|
|||||||
|
|
||||||
// eslint-disable-next-line no-shadow
|
// eslint-disable-next-line no-shadow
|
||||||
interface FormApi<TFormData> {
|
interface FormApi<TFormData> {
|
||||||
|
Provider: (props: { children: any }) => any
|
||||||
getFormProps: () => FormProps
|
getFormProps: () => FormProps
|
||||||
Field: FieldComponent<TFormData, TFormData>
|
Field: FieldComponent<TFormData, TFormData>
|
||||||
useField: UseField<TFormData>
|
useField: UseField<TFormData>
|
||||||
@@ -40,6 +41,9 @@ export function useForm<TData>(opts?: FormOptions<TData>): FormApi<TData> {
|
|||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
const api = new FormApi<TData>(opts)
|
const api = new FormApi<TData>(opts)
|
||||||
|
|
||||||
|
api.Provider = (props) => (
|
||||||
|
<formContext.Provider {...props} value={{ formApi: api }} />
|
||||||
|
)
|
||||||
api.getFormProps = () => {
|
api.getFormProps = () => {
|
||||||
return {
|
return {
|
||||||
onSubmit: formApi.handleSubmit,
|
onSubmit: formApi.handleSubmit,
|
||||||
|
|||||||
Reference in New Issue
Block a user