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 theimperative
option istrue
.options
: Options object.options.key
: An identifier that can be any data type exceptnull
andundefined
. 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 theindependent
option totrue
. Another function of thekey
option is that it can be used withuseReload
hook.options.idle
: Run callback during the browser's idle periods. You can set an object contains atimeout
property to limit the max waiting time.options.imperative
: Run callback manually by calling theload
orforce
function, rather than automatically after the component is mounted.options.independent
: Set this option toture
, event if thekey
are the same, request callbacks in the page are independent and all of them will be executed.options.fallback
: When thecallback
function reject an error and the count of retries has been exhausted, thefallback
function will be called.options.initialData
: Initial data. Used when no cache or request is loading.options.defaultParams
: The default parameter array set forcallback
andfallback
.options.cacheKey
: Cache key to enable caching. It can be any type exceptnull
andundefined
. This hook uses the same cache instance asuseCache
, so you can useuseCache
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 toNumber.MAX_SAFE_INTEGER
, for exampleInfinity
.options.staleTime
: Stale time, in milliseconds. Default is0
. It must be used withcacheKey
. In thestaleTime
period, the data is considered fresh. Callingload(...params)
,reload()
or auto reloading has no effect. You can ignore thestaleTime
option by callingforce(...params)
orreload({ force: true })
methods.options.cacheValidate
: Validate the cache value. If it returns falsy, theinitialData
will be used.options.retry
: Enable retry callback. Default isfalse
.options.retryLimit
: Callback retry limit. Default is3
.options.retryInterval
: Callback retry interval. Default is0
. 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 isfalse
.options.fallbackRetryLimit
: Fallback retry limit. Default is3
.options.fallbackRetryInterval
: Fallback retry interval. It also can be a function.options.polling
: Enable polling when page visible. Default isfalse
.options.pollingInterval
: Polling internal when page visible, in milliseconds. Default is30000
.options.pollingInPageHiding
: Enable polling when page hidden. Default isfalse
. This option is not associated withpolling
option.options.pollingIntervalInPageHiding
: Polling internal when page hidden, in milliseconds. Default is60000
.options.autoReloadWaitTime
: Throttle wait time on auto reload, in milliseconds. Default is10000
.options.autoReloadOnPageShow
: Enable auto reload on page show. Default isfalse
.options.autoReloadOnWindowFocus
: Enable auto reload on window focus. Default isfalse
.options.autoReloadOnNetworkReconnect
: Enable auto reload on network reconnect. Default isfalse
.options.onSuccess
: Success event.options.onFailure
: Failure event.options.onFinally
: Finally event.
Results:
data
: Resolved data. Initial value isundefined
when theinitialData
is not specified.error
: Rejected error. Initial value isnull
.loading
: Loading state. Initial value is opposite to theimperative
option.reloading
: Equal toloading && data !== undefined
. Provided for convenience.initializing
: Equal toloading && data === undefined
. Provided for convenience.load
: Load function. Used in the same way as thecallback
.force
: Similar toload
function, but it ignores thestaleTime
option.reload
: Reload function. When theforce
option is set totrue
, thestaleTime
option will be ignored.cancel
: Cancel everything, including executing request, polling, automatic loading, etc.update
: Update thedata
.
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;