use-query-rd v2.0.3
use-query-rd 
A drop in replacement for apollo client's
useQueryhook with a return type that mimics Elm's RemoteData ADT
Motivation
Tagging network bound data with the state of the request makes impossible states impossible. This is highly desirable as data requests and the subsequent handling of fetched data is a common cause of logic and runtime errors in apps.
Inspo
useQueryRd
This is a @apollo/client specific implementation of RemoteData. useQueryRd is a wrapper around useQuery that returns one additional property, _rd. _rd is a RemoteData that is generic across Success. Failure is not generic as it will always be ApolloError.
useQuery
function useQuery<TData = any, TVariables = OperationVariables>(
query: DocumentNode | TypedDocumentNode<TData, TVariables>,
options?: QueryHookOptions<TData, TVariables>
): QueryResult<TData, TVariables>;useQueryRd
const useQueryRd: <TData, TVariables = OperationVariables>(
query: DocumentNode | TypedDocumentNode<TData, TVariables>,
options?: QueryHookOptions<TData, TVariables> | undefined
) => QueryResult<TData, TVariables> & {
_rd: RemoteData<TData>;
};Pattern Matching
match
Takes a matcher and a RemoteData value. The matcher attributes the RemoteData value to a case and applies the matched function. match support partial matching by supplying a default tag of _. Any RemoteData states no supplied subsequent to the _ will fallback to the function supplied at _. A common use case for this is combining the functions for Initialized and Pending into one "loading" case.
Signature
const match: <T, D>(rd: RemoteData<D>, matcher: Matcher<T, D>) => T;Example
import React from "react";
import { useQueryRd, match } from "use-query-rd";
const ContainerComponent = (): JSX.Element =>
match(useQueryRd<{ data: MyDataType[] }>(GET_DATA_QUERY)._rd, {
_: <Skeleton />,
Failure: (error) => <MyErrorScreen error={err.error} />,
Success: (data: MyDataType) => <MySuccessScreen data={data.data.myData} />,
});
export default ContainerComponent;fold
Takes four functions corresponding to the four tags and a RemoteData value. The function corresponding to the tag of the value is applied.
Signature
const fold: <T, D>(
initialized: () => T,
pending: () => T,
failure: (error: ApolloError) => T,
success: (data: D) => T
) => (_: RemoteData<D>) => T;Example
import React from "react";
import { useQueryRd, fold } from "use-query-rd";
const ContainerComponent = (): JSX.Element =>
fold(
() => <Skeleton />,
() => <Skeleton />,
(error) => <MyErrorScreen error={err.error} />,
(data: MyDataType) => <MySuccessScreen data={data.data.myData} />
)(useQueryRd<{ data: MyDataType[] }>(GET_DATA_QUERY)._rd);
export default ContainerComponent;Mapping
map
Apply the supplied function to the RemoteData if tag is Success, otherwise return original RemoteData
Signature
const map: <T, D>(f: (a: T) => D, fa: RemoteData<T>) => RemoteData<D>;Example
const myInitialData = useSomeData()._rd;
const formattedData = map((res: MyResultType) => {
const manipulatedData = doSomething(res);
return {
myManipulatedData: manipulatedData,
};
}, myInitialData);andMap
Put the results of two RemoteData calls together. Used to make mapX.
Signature
const andMap: <RD1, RD2>(
rd1: RemoteData<RD1>,
rd2: RemoteData<(d: RD1) => RD2>
) => RemoteData<RD2>;map2
Combine two remote data sources with the given function. The result will succeed when (and if) both sources succeed.
Signature
const map2: <D, D2, D3>(
f: (d: D) => (d2: D2) => D3,
rd1: RemoteData<D>,
rd2: RemoteData<D2>
) => RemoteData<D3>;Example
export const Map2Example = () => {
const RD1 = useQueryRd<{ launchesPast: Launch[] }>(ROCKETS_QUERY, {
variables: { limit: 100 },
})._rd;
const RD2 = useQueryRd<{ launchpads: Launchpad[] }>(LAUNCHPADS_QUERY)._rd;
const comb =
(rd1: { launchesPast: Launch[] }) => (rd2: { launchpads: Launchpad[] }) => {
return {
one: rd1,
two: rd2,
};
};
return match(map2(comb, RD1, RD2), {
_: () => <p>Loading...</p>,
Failure: (error) => <p>Error while fetching data ({error.message})</p>,
Success: (data) => (
<>
<p>map2</p>
{JSON.stringify(data)}
</>
),
});Constructors
initialized
Constructs a new RemoteData with a tag of Initialized. This represents a network request yet to be made.
Signature
const initialized: <T = never>() => RemoteData<T>;pending
Constructs a new RemoteData with a tag of Pending. This represents an in flight network request.
Signature
const pending: <T = never>() => RemoteData<T>;failure
Constructs a new RemoteData with a tag of Failure and an ApolloError. While Failure is usually generic in _RemoteData_, useQuery from @apollo/client represents all network failures as ApolloError. Thus, Failure is strictly typed for ApolloError.
Signature
const failure: <T = never>(error: ApolloError) => RemoteData<T>;success
Constructs a new RemoteData with a tag of Success. This represents a resolved network requests with a valid response.
Signature
const success: <D = never>(data: D) => RemoteData<D>;Refinements
isInitialized
Returns true if the rd is an instance of Initialized, false otherwise
Signature
const isInitialized: <D = never>(rd: RemoteData<D>) => rd is Pending;isLoading
Returns true if the rd is an instance of Pending, false otherwise
Signature
const isLoading: <D = never>(rd: RemoteData<D>) => rd is Pending;isFailure
Returns true if the rd is an instance of Failure, false otherwise
Signature
const isFailure: <D = never>(rd: RemoteData<D>) => rd is Failure;isSuccess
Returns true if the rd is an instance of Success, false otherwise
Signature
const isSuccess: <D = never>(rd: RemoteData<D>) => rd is Success<D>;