|
import * as React from "react"; |
|
import { useEffect, useState, useCallback } from "react"; |
|
import { render } from "react-dom"; |
|
|
|
import "./styles.css"; |
|
|
|
type UseSharedLoadingHook = () => { |
|
isLoading: boolean; |
|
compositeLoading: (callback: () => void) => Promise<void>; |
|
increaseAmountRunningTasks: () => void; |
|
decreaseAmountRunningTasks: () => void; |
|
amountRunningTasks: number; |
|
}; |
|
|
|
/** |
|
* Custom hook to access a shared loading state in a component. |
|
* The hook provides a wrapper for multiple composite function executions. |
|
*/ |
|
export const useSharedLoading: UseSharedLoadingHook = () => { |
|
// amount running tasks to gather composite loading executions from consumers of this hook |
|
const [amountRunningTasks, setAmountRunningTasks] = useState<number>(0); |
|
// shared isLoading state for all consuming components |
|
const [isLoading, setIsLoading] = useState<boolean>(true); |
|
|
|
useEffect(() => { |
|
setIsLoading(amountRunningTasks !== 0); |
|
}, [amountRunningTasks]); |
|
|
|
const increaseAmountRunningTasks = () => { |
|
setAmountRunningTasks(oldAmount => oldAmount + 1); |
|
}; |
|
|
|
const decreaseAmountRunningTasks = () => { |
|
setAmountRunningTasks(oldAmount => oldAmount - 1); |
|
}; |
|
|
|
// wrapper function to increase/decrease the amount of running tasks, should be used by the consuming components |
|
const compositeLoading = useCallback(async (callback: () => void) => { |
|
increaseAmountRunningTasks(); |
|
try { |
|
await callback(); |
|
} catch (e) { |
|
console.log(e); |
|
} finally { |
|
decreaseAmountRunningTasks(); |
|
} |
|
}, []); |
|
|
|
return { |
|
isLoading, |
|
compositeLoading, |
|
increaseAmountRunningTasks, |
|
decreaseAmountRunningTasks, |
|
amountRunningTasks |
|
}; |
|
}; |
|
|
|
function App() { |
|
const { |
|
isLoading, |
|
amountRunningTasks, |
|
compositeLoading |
|
} = useSharedLoading(); |
|
|
|
const handleSomeOperation = () => { |
|
compositeLoading(async () => { |
|
await new Promise(resolve => setTimeout(resolve, 1500)); |
|
}); |
|
}; |
|
|
|
const handleAnotherOperation = () => { |
|
compositeLoading(async () => { |
|
await new Promise(resolve => setTimeout(resolve, 1500)); |
|
}); |
|
}; |
|
|
|
return ( |
|
<div className="App"> |
|
<h1>useSharedLoading custom hook</h1> |
|
<p>isLoading: {isLoading.toString()}</p> |
|
{isLoading ? ( |
|
<h4 style={{ color: "orange" }}>I'm loading!</h4> |
|
) : ( |
|
<h4 style={{ color: "green" }}>Not loading!</h4> |
|
)} |
|
<p>{amountRunningTasks}</p> |
|
<button onClick={handleSomeOperation}> |
|
Some operation that takes a while |
|
</button> |
|
<br /> |
|
<br /> |
|
<button onClick={handleAnotherOperation}> |
|
Another operation that takes a while |
|
</button> |
|
</div> |
|
); |
|
} |
|
|
|
const rootElement = document.getElementById("root"); |
|
render(<App />, rootElement); |