Skip to main content

useLoad

Load async data. Usually used for network requests. It accepts a callback function as the first parameter, and the callback function must return a promise object. You can use LoadConfig component to set some default options for this hook.

API

type PromiseResolve<T extends Promise<any>> = T extends Promise<infer P>
? P
: never;
type LoadCallback = (...args: any[]) => Promise<any>;
type LoadData<T extends LoadCallback> = PromiseResolve<ReturnType<T>>;

function useLoad<Callback extends LoadCallback>(
callback: Callback,
deps?: React.DependencyList,
options?: {
key?: {};

idle?: boolean | { timeout?: number };
imperative?: boolean;
independent?: boolean;

fallback?: Callback;
initialData?: LoadData<Callback>;
defaultParams?: Parameters<Callback>;

cacheKey?: {};
cacheTime?: number;
staleTime?: number;
cacheValidate?: (data: any) => boolean;

retry?: boolean;
retryLimit?: number;
retryInterval?: number | ((count: number) => number);
fallbackRetry?: boolean;
fallbackRetryLimit?: number;
fallbackRetryInterval?: number | ((count: number) => number);

polling?: boolean;
pollingInterval?: number;
pollingInPageHiding?: boolean;
pollingIntervalInPageHiding?: number;

autoReloadWaitTime?: number;
autoReloadOnPageShow?: boolean;
autoReloadOnWindowFocus?: boolean;
autoReloadOnNetworkReconnect?: boolean;

onSuccess?: (data: LoadData<Callback>) => void;
onFailure?: (error: any) => void;
onFinally?: () => void;
}
): {
data: LoadData<Callback> | undefined;
error: any;
loading: boolean;
reloading: boolean;
initializing: boolean;

load: (...params: Parameters<Callback>) => ReturnType<Callback>;
force: (...params: Parameters<Callback>) => ReturnType<Callback>;
reload: (options?: { force?: boolean }) => void;
cancel: () => void;

update: (
newData:
| LoadData<Callback>
| ((prevData?: LoadData<Callback>) => LoadData<Callback>)
) => void;
};

Params:

  • callback: A function returns a promise object. It is automatically called after the component is mounted by default.
  • deps: Auto reload dependency list. Default is an empty array. It's not working when the imperative option is true.
  • options: Options object.
    • options.key: An identifier that can be any data type except null and undefined. By default, if multiple requests for the same key appear on the page, only first one request will be sent, and other requests will get data by listening to internal events. You can prevent the default behavior by setting the independent option to true. Another function of the key option is that it can be used with useReload hook.
    • options.idle: Run callback during the browser's idle periods. You can set an object contains a timeout property to limit the max waiting time.
    • options.imperative: Run callback manually by calling the load or force function, rather than automatically after the component is mounted.
    • options.independent: Set this option to ture, event if the key are the same, request callbacks in the page are independent and all of them will be executed.
    • options.fallback: When the callback function reject an error and the count of retries has been exhausted, the fallback function will be called.
    • options.initialData: Initial data. Used when no cache or request is loading.
    • options.defaultParams: The default parameter array set for callback and fallback.
    • options.cacheKey: Cache key to enable caching. It can be any type except null and undefined. This hook uses the same cache instance as useCache, so you can use useCache hook to get data cached by this hook, or set cache data for this hook.
    • options.cacheTime: Cache time, in milliseconds. Default is 5 minutes. You can set a permanent cache by setting this option greater than or equal to Number.MAX_SAFE_INTEGER, for example Infinity.
    • options.staleTime: Stale time, in milliseconds. Default is 0. It must be used with cacheKey. In the staleTime period, the data is considered fresh. Calling load(...params), reload() or auto reloading has no effect. You can ignore the staleTime option by calling force(...params) or reload({ force: true }) methods.
    • options.cacheValidate: Validate the cache value. If it returns falsy, the initialData will be used.
    • options.retry: Enable retry callback. Default is false.
    • options.retryLimit: Callback retry limit. Default is 3.
    • options.retryInterval: Callback retry interval. Default is 0. You can set a function that returns a number value for this option, for example (count) => Math.pow(2, count).
    • options.fallbackRetry: Enable retry fallback. Default is false.
    • options.fallbackRetryLimit: Fallback retry limit. Default is 3.
    • options.fallbackRetryInterval: Fallback retry interval. It also can be a function.
    • options.polling: Enable polling when page visible. Default is false.
    • options.pollingInterval: Polling internal when page visible, in milliseconds. Default is 30000.
    • options.pollingInPageHiding: Enable polling when page hidden. Default is false. This option is not associated with polling option.
    • options.pollingIntervalInPageHiding: Polling internal when page hidden, in milliseconds. Default is 60000.
    • options.autoReloadWaitTime: Throttle wait time on auto reload, in milliseconds. Default is 10000.
    • options.autoReloadOnPageShow: Enable auto reload on page show. Default is false.
    • options.autoReloadOnWindowFocus: Enable auto reload on window focus. Default is false.
    • options.autoReloadOnNetworkReconnect: Enable auto reload on network reconnect. Default is false.
    • options.onSuccess: Success event.
    • options.onFailure: Failure event.
    • options.onFinally: Finally event.

Results:

  • data: Resolved data. Initial value is undefined when the initialData is not specified.
  • error: Rejected error. Initial value is null.
  • loading: Loading state. Initial value is opposite to the imperative option.
  • reloading: Equal to loading && data !== undefined. Provided for convenience.
  • initializing: Equal to loading && data === undefined. Provided for convenience.
  • load: Load function. Used in the same way as the callback.
  • force: Similar to load function, but it ignores the staleTime option.
  • reload: Reload function. When the force option is set to true, the staleTime option will be ignored.
  • cancel: Cancel everything, including executing request, polling, automatic loading, etc.
  • update: Update the data.

Examples

Basic

Loading...
import React from "react";
import { useLoad } from "@lilib/hooks";

const getData = () => {
return new Promise<string>((resolve, reject) => {
setTimeout(() => {
if (Math.random() >= 0.5) {
resolve("@lilib/hooks");
} else {
reject(new Error("rejected"));
}
}, 1000);
});
};

function Example() {
const { data, error, loading } = useLoad(getData);

if (error) {
return <div>Error: {error.message}</div>;
}
if (loading) {
return <div>Loading...</div>;
}
return <div>Data: {data}</div>;
}

export default Example;

Dependency list

Count: Loading...
import React, { useState } from "react";
import { useLoad } from "@lilib/hooks";

function Example() {
const [count, setCount] = useState(0);

const { data, loading } = useLoad(() => {
return new Promise<number>((resolve) => {
setTimeout(() => {
resolve(count);
}, 1000);
});
}, [count]);

return (
<div>
<button onClick={() => setCount(count + 1)}>Increase</button>
Count: {loading ? "Loading..." : data}
</div>
);
}

export default Example;

Imperative (Manual)

Number: undefined
import React from "react";
import { useLoad } from "@lilib/hooks";

const getNumber = () => {
return new Promise<number>((resolve) => {
setTimeout(() => {
resolve(Math.random());
}, 1000);
});
};

function Example() {
const { data, loading, load } = useLoad(getNumber, [], { imperative: true });

return (
<div>
<button onClick={() => load()}>Load</button>
Number: {loading ? "Loading..." : String(data)}
</div>
);
}

export default Example;

Load key

Number: Loading...
Number: Loading...
import React from "react";
import { useLoad, useReload } from "@lilib/hooks";

const getNumber = () => {
return new Promise((resolve) => {
setTimeout(() => {
resolve(Math.random());
}, 1000);
});
};

function Component() {
const { data, loading, reload } = useLoad(getNumber, [], { key: "load-key" });

return (
<div>
Number: {loading ? "Loading..." : data}
<button onClick={() => reload()}>Reload</button>
</div>
);
}

function Example() {
const reload = useReload("load-key");

return (
<>
<Component />
<Component />
<button onClick={() => reload()}>Reload</button>
</>
);
}

export default Example;

Independent

Number: Loading...
Number: Loading...
import React from "react";
import { useLoad, useReload } from "@lilib/hooks";

const getNumber = () => {
return new Promise((resolve) => {
setTimeout(() => {
resolve(Math.random());
}, 1000);
});
};

function Component() {
const { data, loading, reload } = useLoad(getNumber, [], {
key: "load-independent",
independent: true,
});

return (
<div>
Number: {loading ? "Loading..." : data}
<button onClick={() => reload()}>Reload</button>
</div>
);
}

function Example() {
const reload = useReload("load-independent");

return (
<>
<Component />
<Component />
<button onClick={() => reload()}>Reload</button>
</>
);
}

export default Example;

Load params

Count: -1 (Loading...)
import React, { useRef } from "react";
import { useLoad } from "@lilib/hooks";

const getCount = (count: number) => {
return new Promise<number>((resolve) => {
setTimeout(() => {
resolve(count);
}, 1000);
});
};

function Example() {
const countRef = useRef(0);

const { data, loading, load } = useLoad(getCount, [], {
initialData: -1,
defaultParams: [0],
});

return (
<div>
<button onClick={() => load(++countRef.current)}>Load</button>
Count: {data} {loading ? "(Loading...)" : ""}
</div>
);
}

export default Example;

Fallback

Loading...
import React from "react";
import { useLoad } from "@lilib/hooks";

const getNumberError = () => {
return new Promise<number>((resolve, reject) => {
setTimeout(() => {
reject(new Error("rejected"));
}, 1000);
});
};

const getNumberFallback = () => {
return new Promise<number>((resolve) => {
setTimeout(() => {
resolve(Math.random());
}, 1000);
});
};

function Example() {
const { data, error, loading } = useLoad(getNumberError, [], {
fallback: getNumberFallback,
});

if (error) {
return <div>Error: {error.message}</div>;
}
if (loading) {
return <div>Loading...</div>;
}
return <div>Number: {data}</div>;
}

export default Example;

Caching

import React from "react";
import { useLoad, useToggle } from "@lilib/hooks";

const getNumber = () => {
return new Promise<number>((resolve) => {
setTimeout(() => {
resolve(Math.random());
}, 1000);
});
};

function Component() {
const { data, loading } = useLoad(getNumber, [], {
cacheKey: "load-cache-key",
});

return (
<div>
Number: {String(data)} {loading ? "(Loading...)" : ""}
</div>
);
}

function Example() {
const [visible, { toggle }] = useToggle(false);

return (
<div>
<button onClick={() => toggle()}>{visible ? "Hide" : "Show"}</button>
{visible && <Component />}
</div>
);
}

export default Example;

Stale time

staleTime must be used with cacheKey.

Number: undefined
import React from "react";
import { useLoad } from "@lilib/hooks";

const getNumber = () => {
return new Promise<number>((resolve) => {
setTimeout(() => {
resolve(Math.random());
}, 1000);
});
};

function Example() {
const { data, loading, load, force } = useLoad(getNumber, [], {
imperative: true,
cacheKey: "load-stale-time",
staleTime: 5000,
});

return (
<div>
<button onClick={() => load()}>Load</button>
<button onClick={() => force()}>Force</button>
Number: {loading ? "Loading..." : String(data)}
</div>
);
}

export default Example;

Retries

import React from "react";
import { useLoad } from "@lilib/hooks";

const getNumber = () => {
return new Promise((resolve, reject) => {
setTimeout(() => {
if (Math.random() >= 0.8) {
resolve("resolved");
} else {
reject(new Error("rejected"));
}
}, 1000);
});
};

const getNumberFallback = () => {
return new Promise((resolve, reject) => {
setTimeout(() => {
if (Math.random() >= 0.8) {
resolve("fallback resolved");
} else {
reject(new Error("fallback rejected"));
}
}, 1000);
});
};

function Example() {
const { data, error, loading, load } = useLoad(getNumber, [], {
imperative: true,
fallback: getNumberFallback,
retry: true,
fallbackRetry: true,
});

return (
<div>
<button onClick={() => load()}>Load</button>
{loading ? "loading" : error ? error.message : data}
</div>
);
}

export default Example;

Polling

Count:
import React from "react";
import { useLoad } from "@lilib/hooks";

let count = 0;
const getCount = () => {
return new Promise((resolve) => {
setTimeout(() => {
resolve(++count);
}, 1000);
});
};

function Example() {
const { data, loading, load, cancel } = useLoad(getCount, [], {
imperative: true,
polling: true,
pollingInterval: 3000,
});

return (
<div>
<button onClick={() => load()}>Start</button>
<button onClick={() => cancel()}>Cancel</button>
Count: {data} {loading ? "(Loading...)" : ""}
</div>
);
}

export default Example;

Auto reload

Count: (Loading...)
import React from "react";
import { useLoad } from "@lilib/hooks";

let count = 0;
const getCount = () => {
return new Promise((resolve) => {
setTimeout(() => {
resolve(++count);
}, 1000);
});
};

function Example() {
const { data, loading } = useLoad(getCount, [], {
autoReloadOnPageShow: true,
autoReloadOnWindowFocus: true,
autoReloadOnNetworkReconnect: true,
});

return (
<div>
Count: {data} {loading ? "(Loading...)" : ""}
</div>
);
}

export default Example;

Events

import React from "react";
import { useLoad } from "@lilib/hooks";

const getData = () => {
return new Promise<string>((resolve, reject) => {
setTimeout(() => {
if (Math.random() >= 0.5) {
resolve("resolved");
} else {
reject(new Error("rejected"));
}
}, 1000);
});
};

function Example() {
const { load } = useLoad(getData, [], {
imperative: true,
onSuccess: () => {
console.log("onSuccess");
},
onFailure: () => {
console.log("onFailure");
},
onFinally: () => {
console.log("onFinally");
},
});

function handleClick() {
load()
.then(() => {
console.log("success");
})
.catch(() => {
console.log("failed");
});
}

return <button onClick={handleClick}>Load</button>;
}

export default Example;

Prefetch

Number:
import React from "react";
import { useLoad } from "@lilib/hooks";

const getNumber = () => {
return new Promise((resolve) => {
setTimeout(() => {
resolve(Math.random());
}, 1000);
});
};

function RequestInBackground() {
useLoad(getNumber, [], {
idle: true,
cacheKey: "load-prefetch",
cacheTime: Infinity,
});
return null;
}

function Example() {
const { data, loading, load } = useLoad(getNumber, [], {
imperative: true,
cacheKey: "load-prefetch",
staleTime: Infinity,
});

return (
<>
<RequestInBackground />
<button onClick={() => load()}>Load</button>
Number: {data} {loading ? "(Loading...)" : ""}
</>
);
}

export default Example;

Infinite list

    import React, { useState } from "react";
    import { useLoad } from "@lilib/hooks";

    let count = 0;
    const getCount = () => {
    return new Promise<number>((resolve) => {
    setTimeout(() => {
    resolve(++count);
    }, 1000);
    });
    };

    function Example() {
    const [list, setList] = useState<number[]>([]);

    const { loading, load } = useLoad(getCount, [], {
    imperative: true,
    onSuccess: (count) => {
    setList((list) => [...list, count]);
    },
    });

    return (
    <div>
    <button onClick={() => load()}>Load</button>
    <ul>
    {list.map((item) => (
    <li key={item}>{item}</li>
    ))}
    {loading && <li key="loading">Loading...</li>}
    </ul>
    </div>
    );
    }

    export default Example;