hooks icon indicating copy to clipboard operation
hooks copied to clipboard

[RFC] useLoading

Open Dav3rs opened this issue 3 years ago • 8 comments

Hook to track the loading state of async functions.

const [fn, loading] = useLoading(fn)

Example:

function Button({ onClick, children }) {
    const [clickHandler, loading] = useLoading(onClick);
  
    return <button onClick={clickHandler}>{loading ? "Loading..." : children}</button>;
}

Other Example:

function Form({ onSave }) {
    const [handleSave, saving] = useLoading(onSave);
  
    return (
        <>
            ...
            ...
            <button onClick={handleSave}>{saving ? "Saving..." : "Save"}</button>
        </>
    );
}

A posible implementation could look like this:

function useLoading(fn) {
    const [loading, setLoading] = useState(false);
  
    const _fn = useMemoizedFn(async (...args) => {
        setLoading(true);
        const result = await fn(...args);
        setLoading(false);
        return result;
    });
  
    return [_fn, loading];
}

Dav3rs avatar Jul 07 '22 19:07 Dav3rs

what's the difference with useRequest

miracles1919 avatar Jul 08 '22 01:07 miracles1919

useLoading is not for data fetching, but for traking the loading state of any async function, the returned function can be hooked again in the component tree level. Like this:

async function doSomethingFor5Seconds() {
  //...
}

// Parent Component
const [doSomething, doingIt] = useLoading(doSomethingFor5Seconds);

// Child Component
const [handleDo, inProgress] = useLoading(doSomething);

// SubChild Component
const [onClick, loading] = useLoading(handleDo);

// Remember the tuple is [Function, boolean] with any apropiate variable name

Dav3rs avatar Jul 08 '22 13:07 Dav3rs

Currently using this approach, because it stucked loading forever on errors.

export default function useLoading<F extends (...args: any) => Promise<any>>(f: F): [F, boolean] {
  const [loading, setLoading] = useState(false);
  const _f = useMemoizedFn(async (...args: any) => {
    setLoading(true);
    try {
      return await f(...args);
    } catch (error) {
      throw error;
    } finally {
      setLoading(false);
    }
  });
  return [_f as any, loading];
}

Dav3rs avatar Jul 09 '22 02:07 Dav3rs

useLoading is not for data fetching, but for traking the loading state of any async function

useRequest is not only for data fetching, but also can track the loading state of any async function

async function doSomethingFor5Seconds() {
  //...
}

// manual trigger
const { loading, run } = useRequest(doSomethingFor5Seconds, {
  manual: true
});

it stucked loading forever on errors.

useRequest manages the status of loading, error, users can handle different situations according to the status

const {  error, loading } = useRequest(getUsername);

if (error) { ... }
if (loading) { ... }

miracles1919 avatar Jul 09 '22 05:07 miracles1919

Didn't know, but it is still looking to much verbose to rename variables and the word "request" in places where maybe there are not requests. But good to know.

Dav3rs avatar Jul 11 '22 12:07 Dav3rs

I also think useLoading should exist

Sunny-117 avatar Dec 15 '22 03:12 Sunny-117

I also think the component DelayLoading will be better

Sunny-117 avatar Dec 15 '22 03:12 Sunny-117

Indeed!

Dav3rs avatar Dec 15 '22 03:12 Dav3rs