Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 | 1x 3x 3x 3x 3x 2x 2x 2x 2x 1x 1x 1x 2x 3x | import { useState, useEffect } from 'react';
/**
* Fetches and caches the project list from the static /projects.json endpoint.
* Also manages per-card image load tracking used for card entrance animations.
*
* @returns {{ projects: object[], loadedImages: object, setLoadedImages: Function }}
*/
const useProjects = () => {
const [projects, setProjects] = useState([]);
const [loadedImages, setLoadedImages] = useState({});
useEffect(() => {
if (projects.length > 0) return;
let cancelled = false;
(async () => {
try {
const res = await fetch('/projects.json');
Iif (!res.ok) return;
const json = await res.json();
// cancelled guards against setting state after the component has unmounted
Eif (!cancelled && Array.isArray(json) && json.length > 0) setProjects(json);
} catch (e) {}
})();
return () => { cancelled = true; };
}, [projects]);
return { projects, loadedImages, setLoadedImages };
};
export default useProjects;
|