Skip to content

Commit

Permalink
version: 1.0.7
Browse files Browse the repository at this point in the history
  • Loading branch information
imp-dance committed Jun 25, 2024
1 parent 9acbad7 commit 3ed7aa5
Show file tree
Hide file tree
Showing 8 changed files with 443 additions and 195 deletions.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@ryfylke-react/rtk-query-loader",
"version": "1.0.6",
"version": "1.0.7",
"description": "Lets you create reusable, extendable RTK loaders for React components.",
"main": "./dist/cjs/index.js",
"module": "./dist/esm/index.js",
Expand Down
12 changes: 12 additions & 0 deletions src/AwaitLoader.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,18 @@ type AwaitLoaderProps<
args: TProps;
};

/**
* @typedef AwaitLoaderProps
* @type {Object}
* @property {Types.Loader<TProps, TReturn, any, any, any, TArg>} loader The loader to use.
* @property {(data: TReturn) => React.ReactElement} render The render function to use.
* @property {TProps} args The arguments to pass to the loader.
*/

/**
* A component that awaits a loader and renders the data.
* @param {AwaitLoaderProps} args The arguments to pass to the loader.
*/
export const AwaitLoader = <
TProps extends Record<string, any>,
TReturn extends unknown,
Expand Down
3 changes: 3 additions & 0 deletions src/RTKLoader.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@ import { SerializedError } from "@reduxjs/toolkit";
import * as React from "react";
import { CustomLoaderProps } from "./types";

/**
* The default loader component for use with RTK Query Loader.
*/
export function RTKLoader<T>(
props: CustomLoaderProps<T>
): React.ReactElement {
Expand Down
13 changes: 13 additions & 0 deletions src/createLoader.ts
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,19 @@ export const createUseLoader = <
return useLoader;
};

/**
* Creates a `loader` that can be used to fetch data and render error & loading states.
* @example
* const loader = createLoader({
* queriesArg: (props) => props.userId,
* useQueries: (userId) => {
* const user = useGetUserQuery(userId);
* return { queries: { user } };
* },
* onError: (error) => <ErrorView error={error} />,
* onLoading: () => <LoadingView />,
* });
*/
export const createLoader = <
TProps extends unknown,
TQueries extends Types._TQueries,
Expand Down
12 changes: 6 additions & 6 deletions src/createQuery.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,20 +12,20 @@ const requestIdGenerator = () => {

/**
* Creates a query from an async getter function.
*
* ```ts
* @param getter The async function to get the data.
* @param dependencies The dependency array to watch for changes.
* @example
* const query = useCreateQuery(async () => {
* const response = await fetch("https://example.com");
* const response = await fetch(`/users/${userId}`);
* return response.json();
* });
* ```
* }, [userId]);
*/
export const useCreateQuery = <T extends unknown>(
getter: Types.CreateQueryGetter<T>,
dependencies?: any[]
): Types.UseQueryResult<T> => {
const safeDependencies = dependencies ?? [];
const requestId = R.useRef(requestIdGenerator()).current;
const [requestId] = R.useState(() => requestIdGenerator());
const [state, dispatch] = R.useReducer(reducer, {
isLoading: true,
isSuccess: false,
Expand Down
26 changes: 21 additions & 5 deletions src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -274,12 +274,15 @@ export type CreateLoaderArgs<
/** Generates an argument for the `queries` based on component props */
queriesArg?: (props: TProps) => TArg;
/** Determines what to render while loading (with no data to fallback on) */
onLoading?: (props: TProps) => ReactElement;
onLoading?: (
props: TProps,
joinedQuery: UseQueryResult<TReturn>
) => ReactElement;
/** Determines what to render when query fails. */
onError?: (
props: TProps,
error: FetchBaseQueryError | SerializedError,
joinedQuery: UseQueryResult<undefined>
joinedQuery: UseQueryResult<TReturn>
) => ReactElement;
/** @deprecated Using onFetching might result in loss of internal state. Use `whileFetching` instead, or pass the query to the component */
onFetching?: (
Expand Down Expand Up @@ -337,12 +340,15 @@ export type Loader<
/** Generates an argument for the `queries` based on component props */
queriesArg?: (props: TProps) => TArg;
/** Determines what to render while loading (with no data to fallback on) */
onLoading?: (props: TProps) => ReactElement;
onLoading?: (
props: TProps,
joinedQuery: UseQueryResult<TReturn>
) => ReactElement;
/** Determines what to render when query fails. */
onError?: (
props: TProps,
error: SerializedError | FetchBaseQueryError,
joinedQuery: UseQueryResult<undefined>
joinedQuery: UseQueryResult<TReturn>
) => ReactElement;
/** @deprecated Using onFetching might result in loss of internal state. Use `whileFetching` instead, or pass the query to the component */
onFetching?: (
Expand All @@ -352,7 +358,17 @@ export type Loader<
/** Determines what to render besides success-result while query is fetching. */
whileFetching?: WhileFetchingArgs<TProps, TReturn>;
config?: LoaderConfig;
/** Returns a new `Loader` extended from this `Loader`, with given overrides. */
/**
* Creates a `loader` that can be used to fetch data and render error & loading states.
* @example
* const loader = baseLoader.extend({
* queriesArg: (props) => props.userId,
* useQueries: (userId) => {
* const user = useGetUserQuery(userId);
* return { queries: { user } };
* },
* });
*/
extend: <
E_TQueries extends _TQueries = TQueries,
E_TDeferred extends _TDeferred = TDeferred,
Expand Down
18 changes: 12 additions & 6 deletions src/withLoader.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,16 @@ import { FetchBaseQueryError } from "@reduxjs/toolkit/dist/query";
import * as React from "react";
import * as Types from "./types";

/**
* A higher order component that wraps a component and provides it with a loader.
* @param Component The component to wrap with a loader. Second argument is the resolved loader data.
* @param loader The loader to use.
* @returns A component that will load the data and pass it to the wrapped component.
* @example
* const Component = withLoader((props, loaderData) => {
* return <div>{loaderData.queries.user.name}</div>;
* }, loader);
*/
export const withLoader = <
TProps extends Record<string, any>,
TReturn extends unknown,
Expand Down Expand Up @@ -48,16 +58,12 @@ export const withLoader = <
>;
}

const onLoading = loader.onLoading?.(props);
const onLoading = loader.onLoading?.(props, query);

const onError = loader.onError
? (error: SerializedError | FetchBaseQueryError) => {
if (!loader.onError) return <React.Fragment />;
return loader.onError(
props,
error,
query as Types.UseQueryResult<undefined>
);
return loader.onError(props, error, query);
}
: undefined;

Expand Down
Loading

0 comments on commit 3ed7aa5

Please sign in to comment.