T
TanStack3mo ago
evident-indigo

How to get listener fn type?

I have a very simple goal on my form that i use with useAppForm. I have an onChange inside listener and i'm passing a function to it. All i need, is to be able to type the parameters of my custom function to match onChange. Here is an example:
<form.AppField
listeners={{
onChange: onPriceChangeListener,
}}>
<form.AppField
listeners={{
onChange: onPriceChangeListener,
}}>
and my onPriceChangeListener function:
const onPriceChangeListener = (params: WHAT_SHOULD_BE_TYPE?) => {};
const onPriceChangeListener = (params: WHAT_SHOULD_BE_TYPE?) => {};
Thanks!
6 Replies
national-gold
national-gold3mo ago
there's currently no way to extract the exact type to an external function. One reason is that not all listeners have the same data (onSubmit listener vs. onChange). It really depends on what you actually want to extract in this case. What part of the field API do you want to access? do you only need the value? Do you plan on changing other values etc. in this case, you mostly get access to two things: params: { value, fieldApi } value is the value of the field, fieldApi is the typed version of your field to make calls to.
evident-indigo
evident-indigoOP3mo ago
well i need to access fieldApi, for example setValue and of course don't want to manully type everything. I belive those both params { value, fieldApi } are part of the field object, so yeah i'd like get that part so that i can pass it around in a typed manner.
national-gold
national-gold3mo ago
if you have proposals for how to create the typed version, feel free to share! I was thinking of two ways to do it:
// outside components
const myListener = createListener(
formOptions,
'fieldName',
{
onChange: ({ value, fieldApi }) => {/* ... */}
}
)

// inside components
const myListener = form.createListener('fieldName', { /* ... */ })
// outside components
const myListener = createListener(
formOptions,
'fieldName',
{
onChange: ({ value, fieldApi }) => {/* ... */}
}
)

// inside components
const myListener = form.createListener('fieldName', { /* ... */ })
the simpler way is to still use inline functions and pass what's required. For example, handleChange or setValue accept updater functions. So you can do:
function formatMyValue(val: string): string {
return val.toUpperCase()
}

// ...
listeners={{
onBlur: ({ fieldApi }) => fieldApi.handleChange(formatMyValue)
}}
function formatMyValue(val: string): string {
return val.toUpperCase()
}

// ...
listeners={{
onBlur: ({ fieldApi }) => fieldApi.handleChange(formatMyValue)
}}
perhaps a TanStack Table approach wouldn't be that bad either:
const formHelper = createHelper(formOptions)

formHelper.listener('fieldName', { /* ... */ })
formHelper.validator('fieldName', { /* ... */ })

// inside component
const formHelper = form.getHelper();
const formHelper = createHelper(formOptions)

formHelper.listener('fieldName', { /* ... */ })
formHelper.validator('fieldName', { /* ... */ })

// inside component
const formHelper = form.getHelper();
evident-indigo
evident-indigoOP3mo ago
i found this in docs AnyFieldApi this is fieldApi type no?
national-gold
national-gold3mo ago
a very generic one. It has no info on what the fieldApi's value is or what form it came from suitable if you only need meta properties like isTouched etc.
evident-indigo
evident-indigoOP3mo ago
I was thinking something similar to z.infer: ApiForm<typeof form>

Did you find this page helpful?