Skip to content

Commit

Permalink
Merge pull request #38 from aviemet/async
Browse files Browse the repository at this point in the history
feat: fixes async submit not applying transformation or attributes rewrites

test: adds tests, still working on types

feat: 🎸 fixes support for async form submission
  • Loading branch information
aviemet authored Jan 7, 2025
2 parents a86a6da + 23b162e commit e60aad0
Show file tree
Hide file tree
Showing 7 changed files with 542 additions and 172 deletions.
12 changes: 6 additions & 6 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -37,9 +37,9 @@
"lint:fix": "npm run lint -- --fix",
"lint:types": "tsc --noEmit",
"lint:all": "yarn lint && yarn lint:types",
"test": "jest --silent=false",
"test:watch": "jest --watch --silent=false",
"test:coverage": "jest --coverage",
"test": "NODE_NO_WARNINGS=1 jest --silent=false",
"test:watch": "NODE_NO_WARNINGS=1 jest --watch --silent=false",
"test:coverage": "NODE_NO_WARNINGS=1 jest --coverage",
"release": "semantic-release",
"cz": "git-cz"
},
Expand Down Expand Up @@ -67,7 +67,7 @@
"@testing-library/react": "^16.1.0",
"@testing-library/user-event": "^14.5.2",
"@types/jest": "^29.5.14",
"@types/lodash": "^4.17.13",
"@types/lodash": "^4.17.14",
"@types/react": "^19.0.2",
"@types/react-dom": "^19.0.2",
"@typescript-eslint/eslint-plugin": "^8.19.0",
Expand Down Expand Up @@ -100,14 +100,14 @@
"react-dom": "^19.0.0",
"react-test-renderer": "^19.0.0",
"rimraf": "^6.0.1",
"rollup": "^4.29.1",
"rollup": "^4.29.2",
"rollup-plugin-dts": "^6.1.1",
"rollup-plugin-filesize": "^10.0.0",
"rollup-plugin-peer-deps-external": "^2.2.4",
"rollup-plugin-sourcemaps": "^0.6.3",
"rollup-plugin-ts": "^3.4.5",
"rollup-plugin-typescript2": "^0.36.0",
"semantic-release": "^24.2.0",
"semantic-release": "^24.2.1",
"ts-jest": "^29.2.5",
"typescript": "^5.7.2"
},
Expand Down
45 changes: 32 additions & 13 deletions src/Form/index.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
/* eslint-disable react-hooks/exhaustive-deps */
/* eslint-disable react-hooks/rules-of-hooks */
import React, { useCallback, useEffect } from 'react'
import axios from 'axios'
import { type VisitOptions } from '@inertiajs/core'
import useInertiaForm, { NestedObject } from '../useInertiaForm'
import { useForm, type UseFormProps, type HTTPVerb, FormProvider } from './FormProvider'
Expand All @@ -18,10 +19,13 @@ export interface FormProps<TForm> extends PartialHTMLForm {
remember?: boolean
railsAttributes?: boolean
filter?: string[]
onSubmit?: (form: UseFormProps<TForm>) => boolean | void
onChange?: (form: UseFormProps<TForm>) => void
onSubmit?: (form: UseFormProps<TForm>) => boolean | void
onBefore?: (form: UseFormProps<TForm>) => void
onStart?: (form: UseFormProps<TForm>) => void
onSuccess?: (form: UseFormProps<TForm>) => void
onError?: (form: UseFormProps<TForm>) => void
onFinish?: (form: UseFormProps<TForm>) => void
}

const Form = <TForm extends NestedObject>({
Expand All @@ -34,10 +38,13 @@ const Form = <TForm extends NestedObject>({
resetAfterSubmit,
remember = true,
filter,
onSubmit,
onChange,
onSubmit,
onBefore,
onStart,
onSuccess,
onError,
onFinish,
...props
}: Omit<FormProps<TForm>, 'railsAttributes'>) => {
/**
Expand All @@ -61,35 +68,47 @@ const Form = <TForm extends NestedObject>({
const contextValueObject = useCallback((): UseFormProps<TForm> => (
{ ...form, model, method, to, submit }
), [data, form, form.data, form.errors, model, method, to])

/**
* Submits the form. If async prop is true, submits using axios,
* otherwise submits using Inertia's `useForm.submit` method
*/
const submit = async (options?: Partial<VisitOptions>) => {
let shouldSubmit = to && onSubmit?.(contextValueObject()) === false ? false : true

if(shouldSubmit) {
if(async) {
return axios[method](to, form.data)
} else {
return form.submit(method, to, options)
}
}
if(!shouldSubmit) return

return form.submit(method, to, { ...options, async: async === true ? true : false })
}

const handleSubmit = (e: React.FormEvent) => {
e.preventDefault()
e.stopPropagation()

submit({
const submitOptions: Partial<VisitOptions> = {
onSuccess: () => {
if(resetAfterSubmit || (resetAfterSubmit !== false && async === true)) {
form.reset()
}
onSuccess?.(contextValueObject())
},
})
}
if(onBefore) {
submitOptions.onBefore = () => {
onBefore(contextValueObject())
}
}
if(onStart) {
submitOptions.onStart = () => {
onStart(contextValueObject())
}
}
if(onFinish) {
submitOptions.onFinish = () => {
onFinish(contextValueObject())
}
}

submit(submitOptions)
}

// Set values from url search params. Allows for prefilling form data from a link
Expand Down
41 changes: 32 additions & 9 deletions src/useInertiaForm.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,10 +27,13 @@ import {
} from './utils'
import { get, isEqual, isPlainObject, set } from 'lodash'
import { useFormMeta } from './Form/FormMetaWrapper'
import axios, { AxiosResponse } from 'axios'

type VisitOptions = Omit<InertiaVisitOptions, 'errors'> & {
type VisitOptions<TAsync extends boolean = boolean> = (Omit<InertiaVisitOptions, 'errors' | 'onSuccess'> & {
errors?: Record<string, string | string[]>
}
async: TAsync
onSuccess?: (page: TAsync extends true ? AxiosResponse<any, any> : Page<PageProps>) => void
})

type OnChangeCallback = (key: string | undefined, value: unknown, prev: unknown) => void

Expand Down Expand Up @@ -181,8 +184,10 @@ export default function useInertiaForm<TForm>(
// eslint-disable-next-line no-unused-vars
} catch(e) {}

const submit = (method: Method, url: string, options: VisitOptions = {}) => {
const _options = {
const submit = (method: Method, url: string, options: VisitOptions = {
async: false,
}) => {
const _options: VisitOptions = {
...options,
onCancelToken: (token) => {
cancelToken.current = token
Expand Down Expand Up @@ -214,7 +219,7 @@ export default function useInertiaForm<TForm>(
return options.onProgress(event)
}
},
onSuccess: (page: Page<PageProps>) => {
onSuccess: (page) => {
if(isMounted.current) {
setProcessing(false)
setProgress(null)
Expand Down Expand Up @@ -273,11 +278,29 @@ export default function useInertiaForm<TForm>(
if(railsAttributes) {
transformedData = renameObjectWithAttributes(transformedData)
}

if(method === 'delete') {
router.delete(url, { ..._options, data: transformedData as RequestPayload })
if(options.async === true) {
_options.onBefore(undefined)
_options.onStart(undefined)
axios[method](url, transformedData as RequestPayload, {
onUploadProgress: progessEvent => {
_options.onProgress(progessEvent)
},
})
.then(response => {
_options.onSuccess(response)
})
.catch(error => {
_options.onError(error)
})
.finally(() => {
_options.onFinish(undefined)
})
} else {
router[method](url, transformedData as RequestPayload, _options)
if(method === 'delete') {
router.delete(url, { ..._options, data: transformedData as RequestPayload })
} else {
router[method](url, transformedData as RequestPayload, _options)
}
}
}

Expand Down
Loading

0 comments on commit e60aad0

Please sign in to comment.