Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: mount method on FormApi #496

Merged
merged 4 commits into from
Nov 1, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 12 additions & 2 deletions packages/form-core/src/FormApi.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,6 @@ export type FormOptions<TData, ValidatorType> = {
asyncDebounceMs?: number
validator?: ValidatorType
onMount?: ValidateOrFn<TData, ValidatorType>
onMountAsync?: ValidateAsyncFn<TData, ValidatorType>
onMountAsyncDebounceMs?: number
onChange?: ValidateOrFn<TData, ValidatorType>
onChangeAsync?: ValidateAsyncFn<TData, ValidatorType>
onChangeAsyncDebounceMs?: number
Expand Down Expand Up @@ -170,6 +168,18 @@ export class FormApi<TFormData, ValidatorType> {
this.update(opts || {})
}

mount = () => {
if (typeof this.options.onMount === 'function') {
return this.options.onMount(this.state.values, this)
}
if (this.options.validator) {
return (this.options.validator as Validator<TFormData>)().validate(
this.state.values,
this.options.onMount,
)
}
crutchcorn marked this conversation as resolved.
Show resolved Hide resolved
}

update = (options?: FormOptions<TFormData, ValidatorType>) => {
if (!options) return

Expand Down
30 changes: 16 additions & 14 deletions packages/form-core/src/tests/FormApi.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { FieldApi } from '../FieldApi'
describe('form api', () => {
it('should get default form state', () => {
const form = new FormApi()

form.mount()
expect(form.state).toEqual({
values: {},
fieldMeta: {},
Expand All @@ -31,7 +31,7 @@ describe('form api', () => {
name: 'test',
},
})

form.mount()
expect(form.state).toEqual({
values: {
name: 'test',
Expand All @@ -58,7 +58,7 @@ describe('form api', () => {
submissionAttempts: 30,
},
})

form.mount()
expect(form.state).toEqual({
values: {},
fieldMeta: {},
Expand All @@ -83,7 +83,7 @@ describe('form api', () => {
name: 'test',
},
})

form.mount()
form.update({
defaultValues: {
name: 'other',
Expand Down Expand Up @@ -119,7 +119,7 @@ describe('form api', () => {
name: 'test',
},
})

form.mount()
form.setFieldValue('name', 'other')
form.state.submissionAttempts = 300

Expand Down Expand Up @@ -151,7 +151,7 @@ describe('form api', () => {
name: 'test',
},
})

form.mount()
expect(form.getFieldValue('name')).toEqual('test')
})

Expand All @@ -161,7 +161,7 @@ describe('form api', () => {
name: 'test',
},
})

form.mount()
form.setFieldValue('name', 'other')

expect(form.getFieldValue('name')).toEqual('other')
Expand All @@ -173,7 +173,7 @@ describe('form api', () => {
names: ['test'],
},
})

form.mount()
form.pushFieldValue('names', 'other')

expect(form.getFieldValue('names')).toStrictEqual(['test', 'other'])
Expand All @@ -185,7 +185,7 @@ describe('form api', () => {
names: ['one', 'two', 'three'],
},
})

form.mount()
form.insertFieldValue('names', 1, 'other')

expect(form.getFieldValue('names')).toStrictEqual(['one', 'other', 'three'])
Expand All @@ -197,7 +197,7 @@ describe('form api', () => {
names: ['one', 'two', 'three'],
},
})

form.mount()
form.removeFieldValue('names', 1)

expect(form.getFieldValue('names')).toStrictEqual(['one', 'three'])
Expand All @@ -209,7 +209,7 @@ describe('form api', () => {
names: ['one', 'two', 'three'],
},
})

form.mount()
form.swapFieldValues('names', 1, 2)

expect(form.getFieldValue('names')).toStrictEqual(['one', 'three', 'two'])
Expand All @@ -221,7 +221,7 @@ describe('form api', () => {
name: 'test',
},
})

form.mount()
form.setFieldValue('name', 'other')

expect(form.getFieldValue('name')).toEqual('other')
Expand All @@ -237,7 +237,7 @@ describe('form api', () => {
name: 'test',
},
})

form.mount()
expect(form.getFieldValue('name')).toEqual('test')

form.update({
Expand All @@ -255,7 +255,7 @@ describe('form api', () => {
name: 'one',
},
})

form.mount()
expect(form.getFieldValue('name')).toEqual('one')

form.setFieldValue('name', 'two', { touch: true })
Expand Down Expand Up @@ -297,6 +297,8 @@ describe('form api', () => {
onChange: (v) => (v.length > 0 ? undefined : 'required'),
})

form.mount()

field.mount()

field.handleChange('one')
Expand Down
33 changes: 33 additions & 0 deletions packages/react-form/src/tests/useForm.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -124,4 +124,37 @@ describe('useForm', () => {
expect(getByText('Submitted data: OtherName')).toBeInTheDocument(),
)
})

it('should run on form mount', async () => {
function Comp() {
const [formMounted, setFormMounted] = React.useState(false)
const [mountForm, setMountForm] = React.useState(false)

const form = useForm({
defaultValues: {
firstName: 'FirstName',
},
onMount: () => {
setFormMounted(true)
return undefined
},
})

return (
<>
{mountForm ? (
<form.Provider>
<h1>{formMounted ? 'Form mounted' : 'Not mounted'}</h1>
</form.Provider>
) : (
<button onClick={() => setMountForm(true)}>Mount form</button>
)}
</>
)
}

const { getByText } = render(<Comp />)
await user.click(getByText('Mount form'))
await waitFor(() => expect(getByText('Form mounted')).toBeInTheDocument())
})
})
1 change: 1 addition & 0 deletions packages/react-form/src/useForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ export function useForm<TData, FormValidator>(
const api = new FormApi<TData>(opts)

api.Provider = function Provider(props) {
useIsomorphicLayoutEffect(formApi.mount, [])
return <formContext.Provider {...props} value={{ formApi: api }} />
}
api.Field = Field as any
Expand Down
3 changes: 2 additions & 1 deletion packages/solid-form/src/createForm.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import type { FormOptions, FormState } from '@tanstack/form-core'
import { FormApi, functionalUpdate } from '@tanstack/form-core'
import { createComputed, type JSXElement } from 'solid-js'
import { createComputed, onMount, type JSXElement } from 'solid-js'
import { useStore } from '@tanstack/solid-store'
import {
Field,
Expand Down Expand Up @@ -35,6 +35,7 @@ export function createForm<TData, FormValidator>(
const formApi = new FormApi<TData, FormValidator>(options)

formApi.Provider = function Provider(props) {
onMount(formApi.mount)
return <formContext.Provider {...props} value={{ formApi: formApi }} />
}
formApi.Field = Field as any
Expand Down
34 changes: 34 additions & 0 deletions packages/solid-form/src/tests/createForm.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { render, screen, waitFor } from '@solidjs/testing-library'
import '@testing-library/jest-dom'
import userEvent from '@testing-library/user-event'
import { createFormFactory, createForm } from '..'
import { Show, createSignal } from 'solid-js'

const user = userEvent.setup()

Expand Down Expand Up @@ -113,4 +114,37 @@ describe('createForm', () => {
await user.click(getByText('Submit'))
expect(submittedData?.firstName).toEqual('OtherName')
})

it('should run on form mount', async () => {
const [formMounted, setFormMounted] = createSignal(false)
const [mountForm, setMountForm] = createSignal(false)
function Comp() {
const form = createForm(() => ({
defaultValues: {
firstName: 'FirstName',
},
onMount: () => {
setFormMounted(true)
return undefined
},
}))

return (
<Show
when={mountForm()}
fallback={
<button onClick={() => setMountForm(true)}>Mount form</button>
}
>
<form.Provider>
<h1>Form mounted</h1>
</form.Provider>
</Show>
)
}

const { getByText } = render(() => <Comp />)
await user.click(getByText('Mount form'))
expect(formMounted()).toBe(true)
})
})
31 changes: 31 additions & 0 deletions packages/vue-form/src/tests/useForm.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -138,4 +138,35 @@ describe('useForm', () => {
expect(getByText('Submitted data: OtherName')).toBeInTheDocument(),
)
})

it('should run on form mount', async () => {
const Comp = defineComponent(() => {
const formMounted = ref(false)
const mountForm = ref(false)

const form = useForm({
defaultValues: {
firstName: 'FirstName',
},
onMount: () => {
formMounted.value = true
return undefined
},
})
form.provideFormContext()

return () =>
mountForm.value ? (
<form.Provider>
<h1>{formMounted.value ? 'Form mounted' : 'Not mounted'}</h1>
</form.Provider>
) : (
<button onClick={() => (mountForm.value = true)}>Mount form</button>
)
})

const { getByText, findByText } = render(Comp)
await user.click(getByText('Mount form'))
expect(await findByText('Form mounted')).toBeInTheDocument()
})
})
2 changes: 2 additions & 0 deletions packages/vue-form/src/useForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import {
type SlotsType,
type SetupContext,
defineComponent,
onMounted,
} from 'vue-demi'

declare module '@tanstack/form-core' {
Expand Down Expand Up @@ -39,6 +40,7 @@ export function useForm<TData, FormValidator>(

api.Provider = defineComponent(
(_, context) => {
onMounted(formApi.mount)
provideFormContext({ formApi: formApi as never })
return () => context.slots.default!()
},
Expand Down