Remixing remix submission

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

}