queryFn signature does not model fallibility for 'queryError
Closed this issue · 2 comments
Problem
My queryFn
returned promise.t has a result<'a, 'e>
, but I 'queryError
is completely disassociated.
queryFn: ReactQuery_Types.queryFunctionContext<'queryKey, 'pageParam> => Js.Promise.t<'queryData>
Discussion
As rescript users denote, Js.Promise
is more or less worthless for modelling error types, which turns out is pretty critical for JS applications.
I'm currently using aantron/promise which at runtime is the practical mapping between this library's 'queryError
and my 'e
of result<'a, 'e>
. The same could be said for ryyppy's or yawaramin's promise impl's as well.
I'm curious if there's a way to abstract the types here to satisfy the status quo, but also play nicely with result
aware promise impls that care about modelling the error case. perhaps a GADT on the return type could be applicable and processed accordingly in the the useQuery impl?
I was able to do this mapping within my own domain.
external unsafeToJsExn: exn => Js.Exn.t = "%identity"
type jsexn_or_err<'t> =
| JsError(Js.Exn.t)
| Err('t)
type rec queryResultR<'queryError, 'queryData> = {
status: ReactQuery_Types.queryStatus,
// ..snip
error: option<jsexn_or_err<'queryError>>,
}
// useQuery wrapper for doing all Promise modeling with Result types
let useQueryR = (~queryFn) => {
let (err, setErr) = React.useState(_ => None)
let queryFnWithResultHandling = React.useCallback1(opts => {
queryFn(opts)
->Promise.tapError(e => setErr(_ => Some(Err(e))))
->Promise.Js.fromResult
->Promise.Js.toBsPromise
|> Js.Promise.catch(exn => {
let err = exn->%raw("(x => x instanceof Error ? x : new Error(String(x)))")
setErr(_ => Some(JsError(err)))
Js.Promise.reject(err)
})
}, [queryFn])
let res = ReactQuery_Query.useQuery(
ReactQuery_Query.queryOptions(~queryFn=queryFnWithResultHandling, ()),
)
{
status: res.status,
// ...snip
error: err,
}
}
// Demo!
type foo_err = Zed
let foo = () => {
if true {
Ok(1)
} else {
Error(Zed)
}
}
let v = useQueryR(~queryFn=_ => Promise.resolved(foo()))
switch v.error {
| Some(JsError(e')) => () // e' ~ Js.Exn.t
| Some(Err(e')) => () // e' ~ foo_err
| _ => ()
}->ignore
// v.data // option<int>
@cdaringe Sorry for the delay. I was about to suggest you to do something like that. This package is focused on (almost) zero-cost bindings but I understand that the usage of react-query with ReScript could be better if we use built-in structures like variants.
However, I prefer to support zero-cost bindings and provide you a way to create your own custom wrappers instead to make this package a very opinionated bindings that doesn't provide the possibility to create a wrappers or custom hooks based on react-query.