import { useActionData, useNavigation, useSubmit } from '@remix-run/react'
import { useEffect } from 'react'
import { parseSubmissionData } from '~/utils/object'
import { useIndexRouteDetector } from './use-index-route-detector'
import type { RouteSubmission, RouteSubmissionInput, SubmitData } from '~/types/hooks'
/**
* Submit data to the current route from any nested element.
* Prefer this over `useSubmitFetcher` as it allows the `actionData` from `useActionData` hook can be accessed from any nested component in the route no matter how deep it is.
*
* @param input - `object` - The input to use for the route submission
* @param [input._action] - The `_action` to use for the route submission, it will be added to submit data
* @param [input.onSubmitted] - A callback to run when the route submission is submitted
* @returns RouteSubmission
* @example
* ```tsx
* import { useRouteSubmission } from '~/hooks'
*
* export function MyComponent() {
* // An `_action` param should be added to differentiate between multiple submissions
* let [submit, submitting, submitData] = useRouteSubmission({ _action: 'myAction' })
* // or let {submit, submitting, submitData} = useRouteSubmission({ _action: 'myAction' })
*
* let handleClick = () => submit({ name: 'John Doe' })
* let loading = submitting
*
* return (
* <Button loading={loading} onClick={handleClick}>
* Submit
* </Button>
* )
* }
*```
*/
export function useRouteSubmission(input: RouteSubmissionInput): RouteSubmission {
let _submit = useSubmit()
let navigation = useNavigation()
let isIndexRoute = useIndexRouteDetector()
let actionData = useActionData()
let { _action, onSubmitted } = input || {}
let submitData = parseSubmissionData(navigation)
let isActionMatched = submitData?._action === _action
useEffect(() => {
if (isActionMatched && navigation.state === 'loading') {
onSubmitted?.(submitData, actionData)
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [navigation.state])
let submit = (data: SubmitData = {}) => {
let actionURL = window.location.pathname
_submit(
{ data: JSON.stringify({ ...data, _action }) },
{
action: isIndexRoute ? `${actionURL}?index` : actionURL,
method: 'post',
replace: true,
}
)
}
let submitting = isActionMatched && navigation.state === 'submitting'
return Object.defineProperty({ submit, submitting, submitData }, Symbol.iterator, {
enumerable: false,
value: function* () {
yield submit
yield submitting
yield submitData
},
}) as RouteSubmission
}